Reland "[base] Add InsecureRandomGenerator to //base."

This reverts commit d4d0554584d712d4bd32ed7e7cf4cd2c489baad8.

Reason for reland: Removed randomness in the test, which made the test
                   flaky, as expected, since this is the nature of
                   random generator tests.

Original change's description:
> [base] Add InsecureRandomGenerator to //base.
>
> //base only contains a random number generator which typically performs
> a system call for *each* random number. This is undesirable for several
> applications, such as PartitionAlloc.
>
> Introduce a non-cryptographic PRNG, seeded from the OS primitives, which
> is using a fast, user-space only generator. We use XorShift128+, which
> is fast, well-studied and robust. It is also used in V8. It is inherited
> from PartitionAlloc.
>
> There is only one caller right now, PartitionAlloc, in order to not make
> this CL heavier, but this was motivated by forthcoming CLs calling this
> code.
>
> Also add a Chi-Squared test for the generator. This test is well-known,
> including its limitations, but is intended to serve as a sanity
> check. It was able to detect intentional issues with seeding introduced
> during the testing of this CL.
>
> In terms of performance, with the perftest included in this CL, on a
> Intel Haswell Xeon @3.2GHz running Linux 5.10, results are:
> - base::RandUint64(): 784 ns / iteration
> - base::InsecureRandomGenerator: 2ns / iteration
>
> In Chromium, the relative cost of base::RandUint64() may be a bit
> higher, due to sandbox interception of syscalls (read() in this case).
>
> Bug: 1205915
> Change-Id: Iea19ee7968830fe7e8f805e783a09719392ce3ce
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2891773
> Reviewed-by: Daniel Cheng <[email protected]>
> Reviewed-by: Robert Sesek <[email protected]>
> Commit-Queue: Benoit L <[email protected]>
> Cr-Commit-Position: refs/heads/master@{#893800}

Bug: 1205915
Change-Id: Ibd8d796041d8c624e5c04f09a235b975ec07af10
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2999528
Reviewed-by: Austin Sullivan <[email protected]>
Reviewed-by: Daniel Cheng <[email protected]>
Commit-Queue: Benoit L <[email protected]>
Cr-Commit-Position: refs/heads/master@{#898071}
diff --git a/base/rand_util_unittest.cc b/base/rand_util_unittest.cc
index 459da8f..f6ea8c87 100644
--- a/base/rand_util_unittest.cc
+++ b/base/rand_util_unittest.cc
@@ -8,13 +8,17 @@
 #include <stdint.h>
 
 #include <algorithm>
+#include <cmath>
 #include <limits>
 #include <memory>
+#include <vector>
 
 #include "base/logging.h"
 #include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace base {
+
 namespace {
 
 const int kIntMin = std::numeric_limits<int>::min();
@@ -115,8 +119,8 @@
     ++count;
   }
 
-  ASSERT_LT(count, kMaxAttempts) << "Expected average was " <<
-      kExpectedAverage << ", average ended at " << cumulative_average;
+  ASSERT_LT(count, kMaxAttempts) << "Expected average was " << kExpectedAverage
+                                 << ", average ended at " << cumulative_average;
 }
 
 TEST(RandUtilTest, RandUint64ProducesBothValuesOfAllBits) {
@@ -165,6 +169,119 @@
     base::RandBytes(buffer.get(), kTestBufferSize);
   const base::TimeTicks end = base::TimeTicks::Now();
 
-  LOG(INFO) << "RandBytes(" << kTestBufferSize << ") took: "
-            << (end - now).InMicroseconds() << "µs";
+  LOG(INFO) << "RandBytes(" << kTestBufferSize
+            << ") took: " << (end - now).InMicroseconds() << "µs";
 }
+
+TEST(RandUtilTest, InsecureRandomGeneratorProducesBothValuesOfAllBits) {
+  // This tests to see that our underlying random generator is good
+  // enough, for some value of good enough.
+  uint64_t kAllZeros = 0ULL;
+  uint64_t kAllOnes = ~kAllZeros;
+  uint64_t found_ones = kAllZeros;
+  uint64_t found_zeros = kAllOnes;
+
+  InsecureRandomGenerator generator;
+  generator.Seed();
+
+  for (size_t i = 0; i < 1000; ++i) {
+    uint64_t value = generator.RandUint64();
+    found_ones |= value;
+    found_zeros &= value;
+
+    if (found_zeros == kAllZeros && found_ones == kAllOnes)
+      return;
+  }
+
+  FAIL() << "Didn't achieve all bit values in maximum number of tries.";
+}
+
+namespace {
+
+constexpr double kXp1Percent = -2.33;
+constexpr double kXp99Percent = 2.33;
+
+double ChiSquaredCriticalValue(double nu, double x_p) {
+  // From "The Art Of Computer Programming" (TAOCP), Volume 2, Section 3.3.1,
+  // Table 1. This is the asymptotic value for nu > 30, up to O(1 / sqrt(nu)).
+  return nu + sqrt(2. * nu) * x_p + 2. / 3. * (x_p * x_p) - 2. / 3.;
+}
+
+int ExtractBits(uint64_t value, int from_bit, int num_bits) {
+  return (value >> from_bit) & ((1 << num_bits) - 1);
+}
+
+// Performs a Chi-Squared test on a subset of |num_bits| extracted starting from
+// |from_bit| in the generated value.
+//
+// See TAOCP, Volume 2, Section 3.3.1, and
+// https://en.wikipedia.org/wiki/Pearson%27s_chi-squared_test for details.
+//
+// This is only one of the many, many random number generator test we could do,
+// but they are cumbersome, as they are typically very slow, and expected to
+// fail from time to time, due to their probabilistic nature.
+//
+// The generator we use has however been vetted with the BigCrush test suite
+// from Marsaglia, so this should suffice as a smoke test that our
+// implementation is wrong.
+bool ChiSquaredTest(InsecureRandomGenerator& gen,
+                    size_t n,
+                    int from_bit,
+                    int num_bits) {
+  const int range = 1 << num_bits;
+  CHECK_EQ(static_cast<int>(n % range), 0) << "Makes computations simpler";
+  std::vector<size_t> samples(range, 0);
+
+  // Count how many samples pf each value are found. All buckets should be
+  // almost equal if the generator is suitably uniformly random.
+  for (size_t i = 0; i < n; i++) {
+    int sample = ExtractBits(gen.RandUint64(), from_bit, num_bits);
+    samples[sample] += 1;
+  }
+
+  // Compute the Chi-Squared statistic, which is:
+  // \Sum_{k=0}^{range-1} \frac{(count - expected)^2}{expected}
+  double chi_squared = 0.;
+  double expected_count = n / range;
+  for (size_t sample_count : samples) {
+    double deviation = sample_count - expected_count;
+    chi_squared += (deviation * deviation) / expected_count;
+  }
+
+  // The generator should produce numbers that are not too far of (chi_squared
+  // lower than a given quantile), but not too close to the ideal distribution
+  // either (chi_squared is too low).
+  //
+  // See The Art Of Computer Programming, Volume 2, Section 3.3.1 for details.
+  return chi_squared > ChiSquaredCriticalValue(range - 1, kXp1Percent) &&
+         chi_squared < ChiSquaredCriticalValue(range - 1, kXp99Percent);
+}
+
+}  // namespace
+
+TEST(RandUtilTest, InsecureRandomGeneratorChiSquared) {
+  constexpr int kIterations = 50;
+
+  // Specifically test the low bits, which are usually weaker in random number
+  // generators. We don't use them for the 32 bit number generation, but let's
+  // make sure they are still suitable.
+  for (int start_bit : {1, 2, 3, 8, 12, 20, 32, 48, 54}) {
+    int pass_count = 0;
+    for (int i = 0; i < kIterations; i++) {
+      size_t samples = 1 << 16;
+      InsecureRandomGenerator gen;
+      // Fix the seed to make the test non-flaky.
+      gen.SeedForTesting(kIterations + 1);
+      bool pass = ChiSquaredTest(gen, samples, start_bit, 8);
+      pass_count += pass;
+    }
+
+    // We exclude 1% on each side, so we expect 98% of tests to pass, meaning 98
+    // * kIterations / 100. However this is asymptotic, so add a bit of leeway.
+    int expected_pass_count = (kIterations * 98) / 100;
+    EXPECT_GE(pass_count, expected_pass_count - ((kIterations * 2) / 100))
+        << "For start_bit = " << start_bit;
+  }
+}
+
+}  // namespace base