Bug 1826905 [wpt PR 39420] - view-transition: Add WPTs for damage tracking when new...
[gecko.git] / mfbt / Tainting.h
blob2df6176f89162978d58cf89c841e87ef0a4d45b6
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * Creates a Tainted<> wrapper to enforce data validation before use.
9 */
11 #ifndef mozilla_Tainting_h
12 #define mozilla_Tainting_h
14 #include <utility>
15 #include "mozilla/MacroArgs.h"
17 namespace mozilla {
19 template <typename T>
20 class Tainted;
22 namespace ipc {
23 template <typename>
24 struct IPDLParamTraits;
28 * The Tainted<> class allows data to be wrapped and considered 'tainted'; which
29 * requires explicit validation of the data before it can be used for
30 * comparisons or in arithmetic.
32 * Tainted<> objects are intended to be passed down callstacks (still in
33 * Tainted<> form) to whatever location is appropriate to validate (or complete
34 * validation) of the data before finally unwrapping it.
36 * Tainting data ensures that validation actually occurs and is not forgotten,
37 * increase consideration of validation so it can be as strict as possible, and
38 * makes it clear from a code point of view where and what validation is
39 * performed.
42 // ====================================================================
43 // ====================================================================
45 * Simple Tainted<foo> class
47 * Class should not support any de-reference or comparison operator and instead
48 * force all access to the member variable through the MOZ_VALIDATE macros.
50 * While the Coerce() function is publicly accessible on the class, it should
51 * only be used by the MOZ_VALIDATE macros, and static analysis will prevent
52 * it being used elsewhere.
55 template <typename T>
56 class Tainted {
57 private:
58 T mValue;
60 public:
61 explicit Tainted() = default;
63 template <typename U>
64 explicit Tainted(U&& aValue) : mValue(std::forward<U>(aValue)) {}
66 T& Coerce() { return this->mValue; }
67 const T& Coerce() const { return this->mValue; }
69 friend struct mozilla::ipc::IPDLParamTraits<Tainted<T>>;
72 // ====================================================================
73 // ====================================================================
75 * This section contains obscure, non-user-facing C++ to support
76 * variable-argument macros.
78 #define MOZ_TAINT_GLUE(a, b) a b
80 // We use the same variable name in the nested scope, shadowing the outer
81 // scope - this allows the user to write the same variable name in the
82 // macro's condition without using a magic name like 'value'.
84 // We explicitly do not mark it MOZ_MAYBE_UNUSED because the condition
85 // should always make use of tainted_value, not doing so should cause an
86 // unused variable warning. That would only happen when we are bypssing
87 // validation.
89 // The separate bool variable is required to allow condition to be a lambda
90 // expression; lambdas cannot be placed directly inside ASSERTs.
91 #define MOZ_VALIDATE_AND_GET_HELPER3(tainted_value, condition, \
92 assertionstring) \
93 [&]() { \
94 auto& tmp = tainted_value.Coerce(); \
95 auto& tainted_value = tmp; \
96 bool test = (condition); \
97 MOZ_RELEASE_ASSERT(test, assertionstring); \
98 return tmp; \
99 }()
101 #define MOZ_VALIDATE_AND_GET_HELPER2(tainted_value, condition) \
102 MOZ_VALIDATE_AND_GET_HELPER3(tainted_value, condition, \
103 "MOZ_VALIDATE_AND_GET(" #tainted_value \
104 ", " #condition ") has failed")
106 // ====================================================================
107 // ====================================================================
109 * Macros to validate and un-taint a value.
111 * All macros accept the tainted variable as the first argument, and a
112 * condition as the second argument. If the condition is satisfied,
113 * then the value is considered valid.
115 * This file contains documentation and examples for the functions;
116 * more usage examples are present in mfbt/tests/gtest/TestTainting.cpp
120 * MOZ_VALIDATE_AND_GET is the bread-and-butter validation function.
121 * It confirms the value abides by the condition specified and then
122 * returns the untainted value.
124 * If the condition is not satisified, we RELEASE_ASSERT.
126 * Examples:
128 * int bar;
129 * Tainted<int> foo;
130 * int comparisonVariable = 20;
132 * bar = MOZ_VALIDATE_AND_GET(foo, foo < 20);
133 * bar = MOZ_VALIDATE_AND_GET(foo, foo < comparisonVariable);
135 * Note that while the comparison of foo < 20 works inside the macro,
136 * doing so outside the macro (such as with `if (foo < 20)` will
137 * (intentionally) fail during compilation. We do this to ensure that
138 * all validation logic is self-contained inside the macro.
141 * The macro also supports supplying a custom string to the
142 * MOZ_RELEASE_ASSERT. This is strongly encouraged because it
143 * provides the author the opportunity to explain by way of an
144 * english comment what is happening.
146 * Good things to include in the comment:
147 * - What the validation is doing or what it means
148 * - The impact that could occur if validation was bypassed.
149 * e.g. 'This value is used to allocate memory, so sane values
150 * should be enforced.''
151 * - How validation could change in the future to be more or less
152 * restrictive.
154 * Example:
156 * bar = MOZ_VALIDATE_AND_GET(
157 * foo, foo < 20,
158 * "foo must be less than 20 because higher values represent decibel"
159 * "levels greater than a a jet engine inside your ear.");
162 * The condition can also be a lambda function if you need to
163 * define temporary variables or perform more complex validation.
165 * Square brackets represent the capture group - local variables
166 * can be specified here to capture them and use them inside the
167 * lambda. Prefacing the variable with '&' means the variable is
168 * captured by-reference. It is typically better to capture
169 * variables by reference rather than making them parameters.
171 * When using this technique:
172 * - the tainted value must be present and should be captured
173 * by reference. (You could make it a parameter if you wish, but
174 * it's more typing.)
175 * - the entire lambda function must be enclosed in parens
176 * (if you omit this, you might get errors of the form:
177 * 'use of undeclared identifier 'MOZ_VALIDATE_AND_GET_HELPER4')
179 * Example:
181 * bar = MOZ_VALIDATE_AND_GET(foo, ([&foo, &comparisonVariable]() {
182 * bool intermediateResult = externalFunction(foo);
183 * if (intermediateResult || comparisonVariable < 4) {
184 * return true;
186 * return false;
187 * }()));
190 * You can also define a lambda external to the macro if you prefer
191 * this over a static function.
193 * This is possible, and supported, but requires a different syntax.
194 * Instead of specifying the tainted value in the capture group [&foo],
195 * it must be provided as an argument of the unwrapped type.
196 * (The argument name can be anything you choose of course.)
198 * Example:
200 * auto lambda1 = [](int foo) {
201 * bool intermediateResult = externalFunction(foo);
202 * if (intermediateResult) {
203 * return true;
205 * return false;
206 * };
207 * bar = MOZ_VALIDATE_AND_GET(foo, lambda1(foo));
210 * Arguments:
211 * tainted_value - the name of the Tainted<> variable
212 * condition - a comparison involving the tainted value
213 * assertionstring [optional] - A string to include in the RELEASE_ASSERT
215 #define MOZ_VALIDATE_AND_GET(...) \
216 MOZ_TAINT_GLUE(MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_VALIDATE_AND_GET_HELPER, \
217 __VA_ARGS__), \
218 (__VA_ARGS__))
221 * MOZ_IS_VALID is the other most common use, it allows one to test
222 * validity without asserting, for use in a if/else statement.
224 * It supports the same lambda behavior, but does not support a
225 * comment explaining the validation.
227 * Example:
229 * if (MOZ_IS_VALID(foo, foo < 20)) {
230 * ...
234 * Arguments:
235 * tainted_value - the name of the Tainted<> variable
236 * condition - a comparison involving the tainted value
238 #define MOZ_IS_VALID(tainted_value, condition) \
239 [&]() { \
240 auto& tmp = tainted_value.Coerce(); \
241 auto& tainted_value = tmp; \
242 return (condition); \
246 * MOZ_VALIDATE_OR is a shortcut that tests validity and if invalid,
247 * return an alternate value.
249 * Note that the following will not work:
250 * MOZ_RELEASE_ASSERT(MOZ_VALIDATE_OR(foo, foo < 20, 100) == EXPECTED_VALUE);
251 * MOZ_ASSERT(MOZ_VALIDATE_OR(foo, foo < 20, 100) == EXPECTED_VALUE);
252 * This is because internally, many MOZ_VALIDATE macros use lambda
253 * expressions (for variable shadowing purposes) and lambas cannot be
254 * expressions in (potentially) unevaluated operands.
256 * Example:
258 * bar = MOZ_VALIDATE_OR(foo, foo < 20, 100);
261 * Arguments:
262 * tainted_value - the name of the Tainted<> variable
263 * condition - a comparison involving the tainted value
264 * alternate_value - the value to use if the condition is false
266 #define MOZ_VALIDATE_OR(tainted_value, condition, alternate_value) \
267 (MOZ_IS_VALID(tainted_value, condition) ? tainted_value.Coerce() \
268 : alternate_value)
271 * MOZ_FIND_AND_VALIDATE is for testing validity of a tainted value by comparing
272 * it against a list of known safe values. Returns a pointer to the matched
273 * safe value or nullptr if none was found.
275 * Note that for the comparison the macro will loop over the list and that the
276 * current element being tested against is provided as list_item.
278 * Example:
280 * Tainted<int> aId;
281 * NSTArray<Person> list;
282 * const Person* foo = MOZ_FIND_AND_VALIDATE(aId, list_item.id == aId, list);
284 * // Typically you would do nothing if invalid data is passed:
285 * if (MOZ_UNLIKELY(!foo)) {
286 * return;
289 * // Or alternately you can crash on invalid data
290 * MOZ_RELEASE_ASSERT(foo != nullptr, "Invalid person id sent from content
291 * process.");
293 * Arguments:
294 * tainted_value - the name of the Tainted<> variable
295 * condition - a condition involving the tainted value and list_item
296 * validation_list - a list of known safe values to compare against
298 #define MOZ_FIND_AND_VALIDATE(tainted_value, condition, validation_list) \
299 [&]() { \
300 auto& tmp = tainted_value.Coerce(); \
301 auto& tainted_value = tmp; \
302 const auto macro_find_it = \
303 std::find_if(validation_list.cbegin(), validation_list.cend(), \
304 [&](const auto& list_item) { return condition; }); \
305 return macro_find_it != validation_list.cend() ? &*macro_find_it \
306 : nullptr; \
310 * MOZ_NO_VALIDATE allows unsafe removal of the Taint wrapper.
311 * A justification string is required to explain why this is acceptable.
313 * Example:
315 * bar = MOZ_NO_VALIDATE(
316 * foo,
317 * "Value is used to match against a dictionary key in the parent."
318 * "If there's no key present, there won't be a match."
319 * "There is no risk of grabbing a cross-origin value from the dictionary,"
320 * "because the IPC actor is instatiated per-content-process and the "
321 * "dictionary is not shared between actors.");
324 * Arguments:
325 * tainted_value - the name of the Tainted<> variable
326 * justification - a human-understandable string explaining why it is
327 * permissible to omit validation
329 #define MOZ_NO_VALIDATE(tainted_value, justification) \
330 [&tainted_value] { \
331 static_assert(sizeof(justification) > 3, \
332 "Must provide a justification string."); \
333 return tainted_value.Coerce(); \
337 TODO:
339 - Figure out if there are helpers that would be useful for Strings and
340 Principals
341 - Write static analysis to enforce invariants:
342 - No use of .Coerce() except in the header file.
343 - No constant passed to the condition of MOZ_VALIDATE_AND_GET
346 } // namespace mozilla
348 #endif /* mozilla_Tainting_h */