Tainting.h (12758B)
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/. */ 6 7 /* 8 * Creates a Tainted<> wrapper to enforce data validation before use. 9 */ 10 11 #ifndef mozilla_Tainting_h 12 #define mozilla_Tainting_h 13 14 #include <utility> 15 #include "mozilla/MacroArgs.h" 16 17 namespace IPC { 18 template <typename P> 19 struct ParamTraits; 20 } 21 22 namespace mozilla { 23 24 template <typename T> 25 class Tainted; 26 27 /* 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. 31 * 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. 35 * 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. 40 */ 41 42 // ==================================================================== 43 // ==================================================================== 44 /* 45 * Simple Tainted<foo> class 46 * 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. 49 * 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. 53 */ 54 55 template <typename T> 56 class Tainted { 57 private: 58 T mValue; 59 60 public: 61 explicit Tainted() = default; 62 63 template <typename U> 64 explicit Tainted(U&& aValue) : mValue(std::forward<U>(aValue)) {} 65 66 T& Coerce() { return this->mValue; } 67 const T& Coerce() const { return this->mValue; } 68 69 friend struct IPC::ParamTraits<Tainted<T>>; 70 }; 71 72 // ==================================================================== 73 // ==================================================================== 74 /* 75 * This section contains obscure, non-user-facing C++ to support 76 * variable-argument macros. 77 */ 78 #define MOZ_TAINT_GLUE(a, b) a b 79 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'. 83 // 84 // We explicitly do not mark it [[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. 88 // 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 }() 100 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") 105 106 // ==================================================================== 107 // ==================================================================== 108 /* 109 * Macros to validate and un-taint a value. 110 * 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. 114 * 115 * This file contains documentation and examples for the functions; 116 * more usage examples are present in mfbt/tests/gtest/TestTainting.cpp 117 */ 118 119 /* 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. 123 * 124 * If the condition is not satisified, we RELEASE_ASSERT. 125 * 126 * Examples: 127 * 128 * int bar; 129 * Tainted<int> foo; 130 * int comparisonVariable = 20; 131 * 132 * bar = MOZ_VALIDATE_AND_GET(foo, foo < 20); 133 * bar = MOZ_VALIDATE_AND_GET(foo, foo < comparisonVariable); 134 * 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. 139 * 140 * 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. 145 * 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. 153 * 154 * Example: 155 * 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."); 160 * 161 * 162 * The condition can also be a lambda function if you need to 163 * define temporary variables or perform more complex validation. 164 * 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. 170 * 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') 178 * 179 * Example: 180 * 181 * bar = MOZ_VALIDATE_AND_GET(foo, ([&foo, &comparisonVariable]() { 182 * bool intermediateResult = externalFunction(foo); 183 * if (intermediateResult || comparisonVariable < 4) { 184 * return true; 185 * } 186 * return false; 187 * }())); 188 * 189 * 190 * You can also define a lambda external to the macro if you prefer 191 * this over a static function. 192 * 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.) 197 * 198 * Example: 199 * 200 * auto lambda1 = [](int foo) { 201 * bool intermediateResult = externalFunction(foo); 202 * if (intermediateResult) { 203 * return true; 204 * } 205 * return false; 206 * }; 207 * bar = MOZ_VALIDATE_AND_GET(foo, lambda1(foo)); 208 * 209 * 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 214 */ 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__)) 219 220 /* 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. 223 * 224 * It supports the same lambda behavior, but does not support a 225 * comment explaining the validation. 226 * 227 * Example: 228 * 229 * if (MOZ_IS_VALID(foo, foo < 20)) { 230 * ... 231 * } 232 * 233 * 234 * Arguments: 235 * tainted_value - the name of the Tainted<> variable 236 * condition - a comparison involving the tainted value 237 */ 238 #define MOZ_IS_VALID(tainted_value, condition) \ 239 [&]() { \ 240 auto& tmp = tainted_value.Coerce(); \ 241 auto& tainted_value = tmp; \ 242 return (condition); \ 243 }() 244 245 /* 246 * MOZ_VALIDATE_OR is a shortcut that tests validity and if invalid, 247 * return an alternate value. 248 * 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. 255 * 256 * Example: 257 * 258 * bar = MOZ_VALIDATE_OR(foo, foo < 20, 100); 259 * 260 * 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 265 */ 266 #define MOZ_VALIDATE_OR(tainted_value, condition, alternate_value) \ 267 (MOZ_IS_VALID(tainted_value, condition) ? tainted_value.Coerce() \ 268 : alternate_value) 269 270 /* 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. 274 * 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. 277 * 278 * Example: 279 * 280 * Tainted<int> aId; 281 * NSTArray<Person> list; 282 * const Person* foo = MOZ_FIND_AND_VALIDATE(aId, list_item.id == aId, list); 283 * 284 * // Typically you would do nothing if invalid data is passed: 285 * if (MOZ_UNLIKELY(!foo)) { 286 * return; 287 * } 288 * 289 * // Or alternately you can crash on invalid data 290 * MOZ_RELEASE_ASSERT(foo != nullptr, "Invalid person id sent from content 291 * process."); 292 * 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 297 */ 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; \ 307 }() 308 309 /* 310 * MOZ_NO_VALIDATE allows unsafe removal of the Taint wrapper. 311 * A justification string is required to explain why this is acceptable. 312 * 313 * Example: 314 * 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."); 322 * 323 * 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 328 */ 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(); \ 334 }() 335 336 /* 337 TODO: 338 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 344 */ 345 346 } // namespace mozilla 347 348 #endif /* mozilla_Tainting_h */