gecko-dev/mfbt/tests/gtest/TestTainting.cpp

485 строки
16 KiB
C++

/* -*- 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 <math.h>
#include "mozilla/Assertions.h"
#include "mozilla/Range.h"
#include "mozilla/Tainting.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
#include "nsTArray.h"
#include <array>
#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>
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<int> foo = Tainted<int>(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<int> foo = Tainted<int>(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<int> foo = Tainted<int>(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<int> foo = Tainted<int>(EXPECTED_INT);
Tainted<char> baz = Tainted<char>(EXPECTED_CHAR);
//-------------------------------
const mozilla::Array<int, 6> mozarrayWithFoo(0, 5, EXPECTED_INT, 15, 20, 25);
const mozilla::Array<int, 5> 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<TestClass, 5> 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<nsCharPtrHashKey> hashtableWithBaz;
hashtableWithBaz.PutEntry(m);
hashtableWithBaz.PutEntry(o);
hashtableWithBaz.PutEntry(z);
hashtableWithBaz.PutEntry(l);
hashtableWithBaz.PutEntry(a);
nsTHashtable<nsCharPtrHashKey> 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<int> nsTArrayWithFoo = {0, 5, EXPECTED_INT, 15, 20, 25};
const nsTArray<int> 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<int, 6> arrayWithFoo{0, 5, EXPECTED_INT, 15, 20, 25};
const std::array<int, 5> 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<int> dequeWithFoo{0, 5, EXPECTED_INT, 15, 20, 25};
const std::deque<int> 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<int> forwardWithFoo{0, 5, EXPECTED_INT, 15, 20, 25};
const std::forward_list<int> 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<int> listWithFoo{0, 5, EXPECTED_INT, 15, 20, 25};
const std::list<int> 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<std::string, int> mapWithFoo{{
{"zero", 0},
{"five", 5},
{"ten", EXPECTED_INT},
{"fifteen", 15},
{"twenty", 20},
{"twenty-five", 25},
}};
const std::map<std::string, int> 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<int> setWithFoo{0, 5, EXPECTED_INT, 15, 20, 25};
const std::set<int> 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<std::string, int> unordermapWithFoo = {
{"zero", 0}, {"five", 5}, {"ten", EXPECTED_INT},
{"fifteen", 15}, {"twenty", 20}, {"twenty-five", 25},
};
const std::unordered_map<std::string, int> 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<int> unorderedsetWithFoo{0, 5, EXPECTED_INT,
15, 20, 25};
const std::unordered_set<int> 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<int> vectorWithFoo{0, 5, EXPECTED_INT, 15, 20, 25};
const std::vector<int> 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<int> foo = Tainted<int>(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);
}