Bug 1627646 - Avoid creating a Port object when there are no listeners r=mixedpuppy
[gecko.git] / mfbt / Tainting.h
blob238837c596eff8c033523f5724ad1b4b0e217c9b
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 // ================================================
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 // ================================================
75 * Macros to validate and un-taint a value.
77 * All macros accept the tainted variable as the first argument, and a condition
78 * as the second argument. If the condition is satisfied, then the value is
79 * considered valid.
81 * Usage:
82 * Tainted<int> a(50);
85 * int w = MOZ_VALIDATE_AND_GET(a, a < 10);
86 * Here we check that a is greater than 10 and if so, we
87 * return its value. If it is not greater than 10 we MOZ_RELEASE_ASSERT.
89 * Note that while the comparison of a < 10 works inside the macro,
90 * doing so outside the macro (such as with if (a < 10) will
91 * (intentionally) fail. We do this to ensure that all validation logic
92 * is self-contained.
95 * int x = MOZ_VALIDATE_AND_GET(a, a < 10, "a is too large");
96 * The macro also supports supplying a custom string to the
97 * MOZ_RELEASE_ASSERT
100 * int y = MOZ_VALIDATE_AND_GET(a, [&a] {
101 * bool intermediate_result = someFunction(a);
102 * if (intermediate_result) {
103 * return true;
104 * } else {
105 * return someOtherFunction(a);
107 * }());
108 * In this example we use a lambda function to perform a more complicated
109 * validation that cannot be performed succinctly in a single conditional.
112 * int safeOffset = MOZ_VALIDATE_OR(a, a < 10, 0);
113 * MOZ_VALIDATE_OR will provide a default value to use if the tainted value
114 * is invalid.
118 * if (!MOZ_IS_VALID(a, a > 50)) {
119 * return false;
121 * If you want to test validity without triggering an assertion, the
122 * MOZ_IS_VALID macro can be used anywhere as a boolean.
126 #define MOZ_TAINT_GLUE(a, b) a b
128 // We use the same variable name in the nested scope, shadowing the outer
129 // scope - this allows the user to write the same variable name in the
130 // macro's condition without using a magic name like 'value'.
132 // We mark it MOZ_MAYBE_UNUSED because sometimes the condition doesn't
133 // make use of tainted_value, which causes an unused variable warning.
134 // That will only happen when we are bypssing validation, which is a
135 // future ergonomic we will iterate on.
136 #define MOZ_VALIDATE_AND_GET_HELPER3(tainted_value, condition, \
137 assertionstring) \
138 [&tainted_value]() { \
139 auto& tmp = tainted_value.Coerce(); \
140 auto& MOZ_MAYBE_UNUSED tainted_value = tmp; \
141 MOZ_RELEASE_ASSERT((condition), assertionstring); \
142 return tmp; \
145 // This and the next macros are heavy C++ gobblygook to support a two and three
146 // argument variant.
147 #define MOZ_VALIDATE_AND_GET_HELPER2(tainted_value, condition) \
148 MOZ_VALIDATE_AND_GET_HELPER3(tainted_value, condition, \
149 "MOZ_VALIDATE_AND_GET(" #tainted_value \
150 ", " #condition ") has failed")
152 #define MOZ_VALIDATE_AND_GET(...) \
153 MOZ_TAINT_GLUE(MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_VALIDATE_AND_GET_HELPER, \
154 __VA_ARGS__), \
155 (__VA_ARGS__))
157 // This construct uses a lambda expression to create a scope and test the
158 // condition, returning true or false.
159 // We use the same variable-shadowing trick.
160 #define MOZ_IS_VALID(tainted_value, condition) \
161 [&tainted_value]() { \
162 auto& tmp = tainted_value.Coerce(); \
163 auto& MOZ_MAYBE_UNUSED tainted_value = tmp; \
164 return (condition); \
167 // Allows us to test validity and returning a default value if the value is
168 // invalid.
169 #define MOZ_VALIDATE_OR(tainted_value, condition, alternate_value) \
170 (MOZ_IS_VALID(tainted_value, condition) ? tainted_value.Coerce() \
171 : alternate_value)
174 TODO:
176 - Figure out if there are helpers that would be useful for Strings and
177 Principals
178 - Create an unsafe getter that allows passing a string explanation, similar to
179 rlbox
180 - Write static analysis to enforce invariants:
181 - No use of .Coerce() except in the header file.
182 - No constant passed to the condition of MOZ_VALIDATE_AND_GET
185 } // namespace mozilla
187 #endif /* mozilla_Tainting_h */