Bug 1658755 - Create MOZ_FIND_AND_VALIDATE Tainting validation macro; r=tjr,sg
☠☠ backed out by e64f0b3c8194 ☠ ☠
authorjune wilde <jewilde@mozilla.com>
Tue, 17 Nov 2020 04:18:48 +0000
changeset 557570 49e43292c047851a549e5a9a814d8f2578e6ddb4
parent 557569 46f96d192bbde81b758d6bc0662aa07d2b37c9cc
child 557571 59a027d6beb52af2678a30854a4ab0240c6dc182
push id37959
push userbtara@mozilla.com
push dateTue, 17 Nov 2020 21:55:29 +0000
treeherdermozilla-central@9dd0b13d77b9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstjr, sg
bugs1658755
milestone85.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1658755 - Create MOZ_FIND_AND_VALIDATE Tainting validation macro; r=tjr,sg Differential Revision: https://phabricator.services.mozilla.com/D90955
mfbt/Tainting.h
mfbt/tests/TestTainting.cpp
mfbt/tests/gtest/TestTainting.cpp
mfbt/tests/gtest/moz.build
mfbt/tests/moz.build
--- a/mfbt/Tainting.h
+++ b/mfbt/Tainting.h
@@ -108,17 +108,17 @@ class Tainted {
 /*
  * 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.
  *
  * This file contains documentation and examples for the functions;
- * more usage examples are present in mfbt/tests/TestTainting.cpp
+ * more usage examples are present in mfbt/tests/gtest/TestTainting.cpp
  */
 
 /*
  * MOZ_VALIDATE_AND_GET is the bread-and-butter validation function.
  * It confirms the value abides by the condition specified and then
  * returns the untainted value.
  *
  * If the condition is not satisified, we RELEASE_ASSERT.
@@ -263,16 +263,55 @@ class Tainted {
  *   condition - a comparison involving the tainted value
  *   alternate_value - the value to use if the condition is false
  */
 #define MOZ_VALIDATE_OR(tainted_value, condition, alternate_value) \
   (MOZ_IS_VALID(tainted_value, condition) ? tainted_value.Coerce() \
                                           : alternate_value)
 
 /*
+ * MOZ_FIND_AND_VALIDATE is for testing validity of a tainted value by comparing
+ * it against a list of known safe values. Returns a pointer to the matched
+ * safe value or nullptr if none was found.
+ *
+ * Note that for the comparison the macro will loop over the list and that the
+ * current element being tested against is provided as list_item.
+ *
+ * Example:
+ *
+ * Tainted<int> aId;
+ * NSTArray<Person> list;
+ * const Person* foo = MOZ_FIND_AND_VALIDATE(aId, list_item.id == aId, list);
+ *
+ * // Typically you would do nothing if invalid data is passed:
+ * if (MOZ_UNLIKELY(!foo)) {
+ *     return;
+ * }
+ *
+ * // Or alternately you can crash on invalid data
+ * MOZ_RELEASE_ASSERT(foo != nullptr, "Invalid person id sent from content
+ * process.");
+ *
+ * Arguments:
+ *  tainted_value - the name of the Tainted<> variable
+ *  condition - a condition involving the tainted value and list_item
+ *  validation_list - a list of known safe values to compare against
+ */
+#define MOZ_FIND_AND_VALIDATE(tainted_value, condition, validation_list) \
+  [&]() {                                                                \
+    auto& tmp = tainted_value.Coerce();                                  \
+    auto& tainted_value = tmp;                                           \
+    const auto macro_find_it =                                           \
+        std::find_if(validation_list.cbegin(), validation_list.cend(),   \
+                     [&](const auto& list_item) { return condition; });  \
+    return macro_find_it != validation_list.cend() ? &*macro_find_it     \
+                                                   : nullptr;            \
+  }()
+
+/*
  * MOZ_NO_VALIDATE allows unsafe removal of the Taint wrapper.
  * A justification string is required to explain why this is acceptable.
  *
  * Example:
  *
  *  bar = MOZ_NO_VALIDATE(
  *    foo,
  *    "Value is used to match against a dictionary key in the parent."
rename from mfbt/tests/TestTainting.cpp
rename to mfbt/tests/gtest/TestTainting.cpp
--- a/mfbt/tests/TestTainting.cpp
+++ b/mfbt/tests/gtest/TestTainting.cpp
@@ -1,279 +1,484 @@
 /* -*- 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_VALUE 10
+#define EXPECTED_INT 10
+#define EXPECTED_CHAR 'z'
 
 static bool externalFunction(int arg) { return arg > 2; }
 
-static void TestTainting() {
+// ==================================================================
+// MOZ_VALIDATE_AND_GET =============================================
+TEST(Tainting, moz_validate_and_get)
+{
   int bar;
-  Tainted<int> foo = Tainted<int>(EXPECTED_VALUE);
-
-  // ==================================================================
-  // MOZ_VALIDATE_AND_GET =============================================
+  int comparisonVariable = 20;
+  Tainted<int> foo = Tainted<int>(EXPECTED_INT);
 
   bar = MOZ_VALIDATE_AND_GET(foo, foo < 20);
-  MOZ_RELEASE_ASSERT(bar == EXPECTED_VALUE);
+  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.
-  int comparisonVariable = 20;
   bar = MOZ_VALIDATE_AND_GET(foo, foo < comparisonVariable);
-  MOZ_RELEASE_ASSERT(bar == EXPECTED_VALUE);
+  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.");
-  MOZ_RELEASE_ASSERT(bar == EXPECTED_VALUE);
+  ASSERT_EQ(bar, EXPECTED_INT);
 
   // Test an external variable with a comment.
   bar = MOZ_VALIDATE_AND_GET(foo, foo < comparisonVariable, "Test comment");
-  MOZ_RELEASE_ASSERT(bar == EXPECTED_VALUE);
+  ASSERT_EQ(bar, EXPECTED_INT);
 
   // Test an external function with a comment.
   bar = MOZ_VALIDATE_AND_GET(foo, externalFunction(foo), "Test comment");
-  MOZ_RELEASE_ASSERT(bar == EXPECTED_VALUE);
+  ASSERT_EQ(bar, EXPECTED_INT);
 
   // Lambda Tests
-  bar = MOZ_VALIDATE_AND_GET(foo, ([&foo]() {
-                               bool intermediateResult = externalFunction(foo);
-                               if (intermediateResult) {
-                                 return true;
-                               }
-                               return false;
-                             }()));
-  MOZ_RELEASE_ASSERT(bar == EXPECTED_VALUE);
+  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]() {
-                               bool intermediateResult = externalFunction(foo);
-                               if (intermediateResult) {
-                                 return true;
-                               }
-                               return false;
-                             }()),
-                             "This tests a comment");
-  MOZ_RELEASE_ASSERT(bar == EXPECTED_VALUE);
+  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);
-                             if (intermediateResult || comparisonVariable < 4) {
-                               return true;
-                             }
-                             return false;
-                           }()),
-                           "This tests a comment");
-  MOZ_RELEASE_ASSERT(bar == EXPECTED_VALUE);
+  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);
-                             if (intermediateResult || comparisonVariable < 4) {
-                               return true;
-                             }
-                             return false;
-                           }()),
-                           "This tests a comment");
-  MOZ_RELEASE_ASSERT(bar == EXPECTED_VALUE);
+  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) {
+  auto lambda1 = [](int foo) { return externalFunction(foo); };
+
+  auto lambda2 = [&](int foo) {
     bool intermediateResult = externalFunction(foo);
-    if (intermediateResult) {
-      return true;
-    }
-    return false;
+    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));
-  MOZ_RELEASE_ASSERT(bar == EXPECTED_VALUE);
+  ASSERT_EQ(bar, EXPECTED_INT);
 
   // Test with a comment
   bar = MOZ_VALIDATE_AND_GET(foo, lambda1(foo), "Test comment.");
-  MOZ_RELEASE_ASSERT(bar == EXPECTED_VALUE);
+  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);
-    if (intermediateResult || comparisonVariable < 4) {
-      return true;
-    }
-    return false;
+    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, lambda2(foo), "Test comment.");
-  MOZ_RELEASE_ASSERT(bar == EXPECTED_VALUE);
+
+  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);
-    if (intermediateResult || comparisonVariable < 4) {
-      return true;
-    }
-    return false;
+    return intermediateResult || comparisonVariable < 4;
   };
-  bar = MOZ_VALIDATE_AND_GET(foo, lambda3(foo), "Test comment.");
-  MOZ_RELEASE_ASSERT(bar == EXPECTED_VALUE);
-
-  // We can't test MOZ_VALIDATE_AND_GET failing, because that triggers
-  // a release assert.
-
-  // ==================================================================
-  // MOZ_IS_VALID =====================================================
-  if (MOZ_IS_VALID(foo, foo < 20)) {
-    MOZ_RELEASE_ASSERT(true);
-  } else {
-    MOZ_RELEASE_ASSERT(false);
-  }
-
-  if (MOZ_IS_VALID(foo, foo > 20)) {
-    MOZ_RELEASE_ASSERT(false);
-  } else {
-    MOZ_RELEASE_ASSERT(true);
-  }
-
-  if (MOZ_IS_VALID(foo, foo < comparisonVariable)) {
-    MOZ_RELEASE_ASSERT(true);
-  } else {
-    MOZ_RELEASE_ASSERT(false);
-  }
-
-  if (MOZ_IS_VALID(foo, ([&foo]() {
-                     bool intermediateResult = externalFunction(foo);
-                     if (intermediateResult) {
-                       return true;
-                     }
-                     return false;
-                   }()))) {
-    MOZ_RELEASE_ASSERT(true);
-  } else {
-    MOZ_RELEASE_ASSERT(false);
-  }
-
-  if (MOZ_IS_VALID(foo, ([&foo, &comparisonVariable]() {
-                     bool intermediateResult = externalFunction(foo);
-                     if (intermediateResult || comparisonVariable < 4) {
-                       return true;
-                     }
-                     return false;
-                   }()))) {
-    MOZ_RELEASE_ASSERT(true);
-  } else {
-    MOZ_RELEASE_ASSERT(false);
-  }
-
-  if (MOZ_IS_VALID(foo, lambda1(foo))) {
-    MOZ_RELEASE_ASSERT(true);
-  } else {
-    MOZ_RELEASE_ASSERT(false);
-  }
-
-  if (MOZ_IS_VALID(foo, lambda2(foo))) {
-    MOZ_RELEASE_ASSERT(true);
-  } else {
-    MOZ_RELEASE_ASSERT(false);
-  }
-
-  if (MOZ_IS_VALID(foo, lambda3(foo))) {
-    MOZ_RELEASE_ASSERT(true);
-  } else {
-    MOZ_RELEASE_ASSERT(false);
-  }
-
-  // ==================================================================
-  // MOZ_VALIDATE_OR ==================================================
-
-  int result;
-
-  result = MOZ_VALIDATE_OR(foo, foo < 20, 100);
-  MOZ_RELEASE_ASSERT(result == EXPECTED_VALUE);
-
-  result = MOZ_VALIDATE_OR(foo, foo > 20, 100);
-  MOZ_RELEASE_ASSERT(result == 100);
-
-  result = MOZ_VALIDATE_OR(foo, foo < comparisonVariable, 100);
-  MOZ_RELEASE_ASSERT(result == EXPECTED_VALUE);
 
   result = MOZ_VALIDATE_OR(foo, lambda1(foo), 100);
-  MOZ_RELEASE_ASSERT(result == EXPECTED_VALUE);
+  ASSERT_EQ(result, EXPECTED_INT);
 
   result = MOZ_VALIDATE_OR(foo, lambda2(foo), 100);
-  MOZ_RELEASE_ASSERT(result == EXPECTED_VALUE);
+  ASSERT_EQ(result, EXPECTED_INT);
 
   result = MOZ_VALIDATE_OR(foo, lambda3(foo), 100);
-  MOZ_RELEASE_ASSERT(result == EXPECTED_VALUE);
+  ASSERT_EQ(result, EXPECTED_INT);
 
-  result = MOZ_VALIDATE_OR(foo, ([&foo]() {
-                             bool intermediateResult = externalFunction(foo);
-                             if (intermediateResult) {
-                               return true;
-                             }
-                             return false;
-                           }()),
-                           100);
-  MOZ_RELEASE_ASSERT(result == EXPECTED_VALUE);
+  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] {
-                             bool intermediateResult = externalFunction(foo);
-                             if (intermediateResult) {
-                               return true;
-                             }
-                             return false;
-                           }()),
-                           100);
-  MOZ_RELEASE_ASSERT(result == EXPECTED_VALUE);
+  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);
-                             if (intermediateResult || comparisonVariable < 4) {
-                               return true;
-                             }
-                             return false;
-                           }()),
-                           100);
-  MOZ_RELEASE_ASSERT(result == EXPECTED_VALUE);
+  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);
-                             if (intermediateResult || comparisonVariable < 4) {
-                               return true;
-                             }
-                             return false;
-                           }()),
-                           100);
-  MOZ_RELEASE_ASSERT(result == EXPECTED_VALUE);
+  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;
+  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);
 
-  // ==================================================================
-  // MOZ_NO_VALIDATE ==================================================
+  //-------------------------------
+  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.");
-  MOZ_RELEASE_ASSERT(result == EXPECTED_VALUE);
+  ASSERT_TRUE(result == EXPECTED_INT);
 }
-
-int main() {
-  TestTainting();
-
-  return 0;
-}
--- a/mfbt/tests/gtest/moz.build
+++ b/mfbt/tests/gtest/moz.build
@@ -4,28 +4,29 @@
 # 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/.
 
 UNIFIED_SOURCES += [
     "TestBuffer.cpp",
     "TestLinkedList.cpp",
     "TestReverseIterator.cpp",
     "TestSpan.cpp",
+    "TestTainting.cpp",
 ]
 
 SOURCES += [
     "TestAlgorithm.cpp",
     "TestInitializedOnce.cpp",
     "TestMainThreadWeakPtr.cpp",
     "TestResultExtensions.cpp",
 ]
 
 if not CONFIG["MOZILLA_OFFICIAL"]:
     UNIFIED_SOURCES += [
         # MOZ_DBG is not defined in MOZILLA_OFFICIAL builds.
         "TestMozDbg.cpp",
     ]
 
 # LOCAL_INCLUDES += [
-#    '../../base',
+#    "../../base",
 # ]
 
 FINAL_LIBRARY = "xul-gtest"
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -55,17 +55,16 @@ CppUnitTests(
         "TestRollingMean",
         "TestSaturate",
         "TestScopeExit",
         "TestSegmentedVector",
         "TestSHA1",
         "TestSmallPointerArray",
         "TestSplayTree",
         "TestSPSCQueue",
-        "TestTainting",
         "TestTemplateLib",
         "TestTextUtils",
         "TestThreadSafeWeakPtr",
         "TestTuple",
         "TestTypedEnum",
         "TestTypeTraits",
         "TestUniquePtr",
         "TestVariant",