tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */