/* -*- 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/. */ #include "gtest/gtest.h" #include #include "mozilla/Array.h" #include "mozilla/Assertions.h" #include "mozilla/Range.h" #include "mozilla/Tainting.h" #include "nsTHashtable.h" #include "nsHashKeys.h" #include "nsTArray.h" #include #include #include #include #include #include #include #include #include using mozilla::Tainted; #define EXPECTED_INT 10 #define EXPECTED_CHAR 'z' static bool externalFunction(int arg) { return arg > 2; } // ================================================================== // MOZ_VALIDATE_AND_GET ============================================= TEST(Tainting, moz_validate_and_get) { int bar; int comparisonVariable = 20; Tainted foo = Tainted(EXPECTED_INT); bar = MOZ_VALIDATE_AND_GET(foo, foo < 20); ASSERT_EQ(bar, EXPECTED_INT); // This test is for comparison to an external variable, testing the // default capture mode of the lambda used inside the macro. bar = MOZ_VALIDATE_AND_GET(foo, foo < comparisonVariable); ASSERT_EQ(bar, EXPECTED_INT); bar = MOZ_VALIDATE_AND_GET( foo, foo < 20, "foo must be less than 20 because higher values represent decibel" "levels greater than a a jet engine inside your ear."); ASSERT_EQ(bar, EXPECTED_INT); // Test an external variable with a comment. bar = MOZ_VALIDATE_AND_GET(foo, foo < comparisonVariable, "Test comment"); ASSERT_EQ(bar, EXPECTED_INT); // Test an external function with a comment. bar = MOZ_VALIDATE_AND_GET(foo, externalFunction(foo), "Test comment"); ASSERT_EQ(bar, EXPECTED_INT); // Lambda Tests bar = MOZ_VALIDATE_AND_GET(foo, ([&foo]() { return externalFunction(foo); }())); ASSERT_EQ(bar, EXPECTED_INT); // This test is for the lambda variant with a supplied assertion // string. bar = MOZ_VALIDATE_AND_GET(foo, ([&foo]() { return externalFunction(foo); }()), "This tests a comment"); ASSERT_EQ(bar, EXPECTED_INT); // This test is for the lambda variant with a captured variable bar = MOZ_VALIDATE_AND_GET(foo, ([&foo, &comparisonVariable] { bool intermediateResult = externalFunction(foo); return intermediateResult || comparisonVariable < 4; }()), "This tests a comment"); ASSERT_EQ(bar, EXPECTED_INT); // This test is for the lambda variant with full capture mode bar = MOZ_VALIDATE_AND_GET(foo, ([&] { bool intermediateResult = externalFunction(foo); return intermediateResult || comparisonVariable < 4; }()), "This tests a comment"); ASSERT_EQ(bar, EXPECTED_INT); // External lambdas auto lambda1 = [](int foo) { return externalFunction(foo); }; auto lambda2 = [&](int foo) { bool intermediateResult = externalFunction(foo); return intermediateResult || comparisonVariable < 4; }; // Test with an explicit capture auto lambda3 = [&comparisonVariable](int foo) { bool intermediateResult = externalFunction(foo); return intermediateResult || comparisonVariable < 4; }; bar = MOZ_VALIDATE_AND_GET(foo, lambda1(foo)); ASSERT_EQ(bar, EXPECTED_INT); // Test with a comment bar = MOZ_VALIDATE_AND_GET(foo, lambda1(foo), "Test comment."); ASSERT_EQ(bar, EXPECTED_INT); // Test with a default capture mode bar = MOZ_VALIDATE_AND_GET(foo, lambda2(foo), "Test comment."); ASSERT_EQ(bar, EXPECTED_INT); bar = MOZ_VALIDATE_AND_GET(foo, lambda3(foo), "Test comment."); ASSERT_EQ(bar, EXPECTED_INT); // We can't test MOZ_VALIDATE_AND_GET failing, because that triggers // a release assert. } // ================================================================== // MOZ_IS_VALID ===================================================== TEST(Tainting, moz_is_valid) { int comparisonVariable = 20; Tainted foo = Tainted(EXPECTED_INT); ASSERT_TRUE(MOZ_IS_VALID(foo, foo < 20)); ASSERT_FALSE(MOZ_IS_VALID(foo, foo > 20)); ASSERT_TRUE(MOZ_IS_VALID(foo, foo < comparisonVariable)); ASSERT_TRUE( MOZ_IS_VALID(foo, ([&foo]() { return externalFunction(foo); }()))); ASSERT_TRUE(MOZ_IS_VALID(foo, ([&foo, &comparisonVariable]() { bool intermediateResult = externalFunction(foo); return intermediateResult || comparisonVariable < 4; }()))); // External lambdas auto lambda1 = [](int foo) { return externalFunction(foo); }; auto lambda2 = [&](int foo) { bool intermediateResult = externalFunction(foo); return intermediateResult || comparisonVariable < 4; }; // Test with an explicit capture auto lambda3 = [&comparisonVariable](int foo) { bool intermediateResult = externalFunction(foo); return intermediateResult || comparisonVariable < 4; }; ASSERT_TRUE(MOZ_IS_VALID(foo, lambda1(foo))); ASSERT_TRUE(MOZ_IS_VALID(foo, lambda2(foo))); ASSERT_TRUE(MOZ_IS_VALID(foo, lambda3(foo))); } // ================================================================== // MOZ_VALIDATE_OR ================================================== TEST(Tainting, moz_validate_or) { int result; int comparisonVariable = 20; Tainted foo = Tainted(EXPECTED_INT); result = MOZ_VALIDATE_OR(foo, foo < 20, 100); ASSERT_EQ(result, EXPECTED_INT); result = MOZ_VALIDATE_OR(foo, foo > 20, 100); ASSERT_EQ(result, 100); result = MOZ_VALIDATE_OR(foo, foo < comparisonVariable, 100); ASSERT_EQ(result, EXPECTED_INT); // External lambdas auto lambda1 = [](int foo) { return externalFunction(foo); }; auto lambda2 = [&](int foo) { bool intermediateResult = externalFunction(foo); return intermediateResult || comparisonVariable < 4; }; // Test with an explicit capture auto lambda3 = [&comparisonVariable](int foo) { bool intermediateResult = externalFunction(foo); return intermediateResult || comparisonVariable < 4; }; result = MOZ_VALIDATE_OR(foo, lambda1(foo), 100); ASSERT_EQ(result, EXPECTED_INT); result = MOZ_VALIDATE_OR(foo, lambda2(foo), 100); ASSERT_EQ(result, EXPECTED_INT); result = MOZ_VALIDATE_OR(foo, lambda3(foo), 100); ASSERT_EQ(result, EXPECTED_INT); result = MOZ_VALIDATE_OR(foo, ([&foo]() { return externalFunction(foo); }()), 100); ASSERT_EQ(result, EXPECTED_INT); // This test is for the lambda variant with a supplied assertion // string. result = MOZ_VALIDATE_OR(foo, ([&foo] { return externalFunction(foo); }()), 100); ASSERT_EQ(result, EXPECTED_INT); // This test is for the lambda variant with a captured variable result = MOZ_VALIDATE_OR(foo, ([&foo, &comparisonVariable] { bool intermediateResult = externalFunction(foo); return intermediateResult || comparisonVariable < 4; }()), 100); ASSERT_EQ(result, EXPECTED_INT); // This test is for the lambda variant with full capture mode result = MOZ_VALIDATE_OR(foo, ([&] { bool intermediateResult = externalFunction(foo); return intermediateResult || comparisonVariable < 4; }()), 100); ASSERT_EQ(result, EXPECTED_INT); } // ================================================================== // MOZ_FIND_AND_VALIDATE ============================================ TEST(Tainting, moz_find_and_validate) { Tainted foo = Tainted(EXPECTED_INT); Tainted baz = Tainted(EXPECTED_CHAR); //------------------------------- const mozilla::Array mozarrayWithFoo(0, 5, EXPECTED_INT, 15, 20, 25); const mozilla::Array mozarrayWithoutFoo(0, 5, 15, 20, 25); ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, mozarrayWithFoo) == mozarrayWithFoo[2]); ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, mozarrayWithoutFoo) == nullptr); //------------------------------- class TestClass { public: int a; int b; TestClass(int a, int b) { this->a = a; this->b = b; } bool operator==(const TestClass& other) const { return this->a == other.a && this->b == other.b; } }; const mozilla::Array mozarrayOfClassesWithFoo( TestClass(0, 1), TestClass(2, 3), TestClass(EXPECTED_INT, EXPECTED_INT), TestClass(4, 5), TestClass(6, 7)); ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE( foo, foo == list_item.a && foo == list_item.b, mozarrayOfClassesWithFoo) == mozarrayOfClassesWithFoo[2]); ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE( foo, (foo == list_item.a && foo == list_item.b), mozarrayOfClassesWithFoo) == mozarrayOfClassesWithFoo[2]); ASSERT_TRUE( *MOZ_FIND_AND_VALIDATE( foo, (foo == list_item.a && foo == list_item.b && externalFunction(foo)), mozarrayOfClassesWithFoo) == mozarrayOfClassesWithFoo[2]); ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE( foo, ([](int tainted_val, TestClass list_item) { return tainted_val == list_item.a && tainted_val == list_item.b; }(foo, list_item)), mozarrayOfClassesWithFoo) == mozarrayOfClassesWithFoo[2]); auto lambda4 = [](int tainted_val, TestClass list_item) { return tainted_val == list_item.a && tainted_val == list_item.b; }; ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, lambda4(foo, list_item), mozarrayOfClassesWithFoo) == mozarrayOfClassesWithFoo[2]); //------------------------------- const char m[] = "m"; const char o[] = "o"; const char z[] = {EXPECTED_CHAR, '\0'}; const char l[] = "l"; const char a[] = "a"; nsTHashtable hashtableWithBaz; hashtableWithBaz.PutEntry(m); hashtableWithBaz.PutEntry(o); hashtableWithBaz.PutEntry(z); hashtableWithBaz.PutEntry(l); hashtableWithBaz.PutEntry(a); nsTHashtable hashtableWithoutBaz; hashtableWithoutBaz.PutEntry(m); hashtableWithoutBaz.PutEntry(o); hashtableWithoutBaz.PutEntry(l); hashtableWithoutBaz.PutEntry(a); ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(baz, *list_item.GetKey() == baz, hashtableWithBaz) == hashtableWithBaz.GetEntry(z)); ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(baz, *list_item.GetKey() == baz, hashtableWithoutBaz) == nullptr); //------------------------------- const nsTArray nsTArrayWithFoo = {0, 5, EXPECTED_INT, 15, 20, 25}; const nsTArray nsTArrayWithoutFoo = {0, 5, 15, 20, 25}; ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, nsTArrayWithFoo) == nsTArrayWithFoo[2]); ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, nsTArrayWithoutFoo) == nullptr); //------------------------------- const std::array arrayWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; const std::array arrayWithoutFoo{0, 5, 15, 20, 25}; ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, arrayWithFoo) == arrayWithFoo[2]); ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, arrayWithoutFoo) == nullptr); //------------------------------- const std::deque dequeWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; const std::deque dequeWithoutFoo{0, 5, 15, 20, 25}; ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, dequeWithFoo) == dequeWithFoo[2]); ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, dequeWithoutFoo) == nullptr); //------------------------------- const std::forward_list forwardWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; const std::forward_list forwardWithoutFoo{0, 5, 15, 20, 25}; auto forwardListIt = forwardWithFoo.begin(); std::advance(forwardListIt, 2); ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, forwardWithFoo) == *forwardListIt); ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, forwardWithoutFoo) == nullptr); //------------------------------- const std::list listWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; const std::list listWithoutFoo{0, 5, 15, 20, 25}; auto listIt = listWithFoo.begin(); std::advance(listIt, 2); ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, listWithFoo) == *listIt); ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, listWithoutFoo) == nullptr); //------------------------------- const std::map mapWithFoo{{ {"zero", 0}, {"five", 5}, {"ten", EXPECTED_INT}, {"fifteen", 15}, {"twenty", 20}, {"twenty-five", 25}, }}; const std::map mapWithoutFoo{{ {"zero", 0}, {"five", 5}, {"fifteen", 15}, {"twenty", 20}, {"twenty-five", 25}, }}; const auto map_it = mapWithFoo.find("ten"); ASSERT_TRUE( MOZ_FIND_AND_VALIDATE(foo, list_item.second == foo, mapWithFoo)->second == map_it->second); ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item.second == foo, mapWithoutFoo) == nullptr); //------------------------------- const std::set setWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; const std::set setWithoutFoo{0, 5, 15, 20, 25}; auto setIt = setWithFoo.find(EXPECTED_INT); ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, setWithFoo) == *setIt); ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, setWithoutFoo) == nullptr); //------------------------------- const std::unordered_map unordermapWithFoo = { {"zero", 0}, {"five", 5}, {"ten", EXPECTED_INT}, {"fifteen", 15}, {"twenty", 20}, {"twenty-five", 25}, }; const std::unordered_map unordermapWithoutFoo{{ {"zero", 0}, {"five", 5}, {"fifteen", 15}, {"twenty", 20}, {"twenty-five", 25}, }}; auto unorderedMapIt = unordermapWithFoo.find("ten"); ASSERT_TRUE( MOZ_FIND_AND_VALIDATE(foo, list_item.second == foo, unordermapWithFoo) ->second == unorderedMapIt->second); ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item.second == foo, unordermapWithoutFoo) == nullptr); //------------------------------- const std::unordered_set unorderedsetWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; const std::unordered_set unorderedsetWithoutFoo{0, 5, 15, 20, 25}; auto unorderedSetIt = unorderedsetWithFoo.find(EXPECTED_INT); ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, unorderedsetWithFoo) == *unorderedSetIt); ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, unorderedsetWithoutFoo) == nullptr); //------------------------------- const std::vector vectorWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; const std::vector vectorWithoutFoo{0, 5, 15, 20, 25}; ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, vectorWithFoo) == vectorWithFoo[2]); ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, vectorWithoutFoo) == nullptr); } // ================================================================== // MOZ_NO_VALIDATE ================================================== TEST(Tainting, moz_no_validate) { int result; Tainted foo = Tainted(EXPECTED_INT); result = MOZ_NO_VALIDATE( foo, "Value is used to match against a dictionary key in the parent." "If there's no key present, there won't be a match." "There is no risk of grabbing a cross-origin value from the dictionary," "because the IPC actor is instatiated per-content-process and the " "dictionary is not shared between actors."); ASSERT_TRUE(result == EXPECTED_INT); }