/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Creates a Tainted<> wrapper to enforce data validation before use. */ #ifndef mozilla_Tainting_h #define mozilla_Tainting_h #include #include "mozilla/MacroArgs.h" namespace mozilla { /* * The Tainted<> class allows data to be wrapped and considered 'tainted'; which * requires explicit validation of the data before it can be used for * comparisons or in arithmetic. * * Tainted<> objects are intended to be passed down callstacks (still in * Tainted<> form) to whatever location is appropriate to validate (or complete * validation) of the data before finally unwrapping it. * * Tainting data ensures that validation actually occurs and is not forgotten, * increase consideration of validation so it can be as strict as possible, and * makes it clear from a code point of view where and what validation is * performed. */ // ================================================ /* * Simple Tainted class * * Class should not support any de-reference or comparison operator and instead * force all access to the member variable through the MOZ_VALIDATE macros. * * While the Coerce() function is publicly accessible on the class, it should * only be used by the MOZ_VALIDATE macros, and static analysis will prevent * it being used elsewhere. */ template class Tainted { private: T mValue; public: template explicit Tainted(U&& aValue) : mValue(std::forward(aValue)) {} T& Coerce() { return this->mValue; } }; // ================================================ /* * Macros to validate and un-taint a value. * * All macros accept the tainted variable as the first argument, and a condition * as the second argument. If the condition is satisfied, then the value is * considered valid. * * Usage: * Tainted a(50); * * * int w = MOZ_VALIDATE_AND_GET(a, a < 10); * Here we check that a is greater than 10 and if so, we * return its value. If it is not greater than 10 we MOZ_RELEASE_ASSERT. * * Note that while the comparison of a < 10 works inside the macro, * doing so outside the macro (such as with if (a < 10) will * (intentionally) fail. We do this to ensure that all validation logic * is self-contained. * * * int x = MOZ_VALIDATE_AND_GET(a, a < 10, "a is too large"); * The macro also supports supplying a custom string to the * MOZ_RELEASE_ASSERT * * * int y = MOZ_VALIDATE_AND_GET(a, [&a] { * bool intermediate_result = someFunction(a); * if (intermediate_result) { * return true; * } else { * return someOtherFunction(a); * } * }()); * In this example we use a lambda function to perform a more complicated * validation that cannot be performed succinctly in a single conditional. * * * int safeOffset = MOZ_VALIDATE_OR(a, a < 10, 0); * MOZ_VALIDATE_OR will provide a default value to use if the tainted value * is invalid. * * * * if (!MOZ_IS_VALID(a, a > 50)) { * return false; * } * If you want to test validity without triggering an assertion, the * MOZ_IS_VALID macro can be used anywhere as a boolean. * */ #define MOZ_TAINT_GLUE(a, b) a b // We use the same variable name in the nested scope, shadowing the outer // scope - this allows the user to write the same variable name in the // macro's condition without using a magic name like 'value'. #define MOZ_VALIDATE_AND_GET_HELPER3(tainted_value, condition, \ assertionstring) \ [&tainted_value]() { \ auto& tmp = tainted_value.Coerce(); \ auto& tainted_value = tmp; \ MOZ_RELEASE_ASSERT((condition), assertionstring); \ return tmp; \ }() // This and the next macros are heavy C++ gobblygook to support a two and three // argument variant. #define MOZ_VALIDATE_AND_GET_HELPER2(tainted_value, condition) \ MOZ_VALIDATE_AND_GET_HELPER3(tainted_value, condition, \ "MOZ_VALIDATE_AND_GET(" #tainted_value \ ", " #condition ") has failed") #define MOZ_VALIDATE_AND_GET(...) \ MOZ_TAINT_GLUE(MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_VALIDATE_AND_GET_HELPER, \ __VA_ARGS__), \ (__VA_ARGS__)) // This construct uses a lambda expression to create a scope and test the // condition, returning true or false. // We use the same variable-shadowing trick. #define MOZ_IS_VALID(tainted_value, condition) \ [&tainted_value]() { \ auto& tmp = tainted_value.Coerce(); \ auto& tainted_value = tmp; \ return (condition); \ }() // Allows us to test validity and returning a default value if the value is // invalid. #define MOZ_VALIDATE_OR(tainted_value, condition, alternate_value) \ (MOZ_IS_VALID(tainted_value, condition) ? tainted_value.Coerce() \ : alternate_value) /* TODO: - Figure out if there are helpers that would be useful for Strings and Principals - Create an unsafe getter that allows passing a string explanation, similar to rlbox - Write static analysis to enforce invariants: - No use of .Coerce() except in the header file. - No constant passed to the condition of MOZ_VALIDATE_AND_GET */ } // namespace mozilla #endif /* mozilla_Tainting_h */