123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- // Copyright 2017 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/allocator/partition_allocator/address_space_randomization.h"
- #include <cstdint>
- #include <vector>
- #include "base/allocator/partition_allocator/page_allocator.h"
- #include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h"
- #include "base/allocator/partition_allocator/partition_alloc_check.h"
- #include "base/allocator/partition_allocator/random.h"
- #include "build/build_config.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #if BUILDFLAG(IS_WIN)
- #include <windows.h>
- #include "base/win/windows_version.h"
- // versionhelpers.h must be included after windows.h.
- #include <versionhelpers.h>
- #endif
- namespace partition_alloc {
- namespace {
- uintptr_t GetMask() {
- uintptr_t mask = internal::ASLRMask();
- #if defined(ARCH_CPU_64_BITS)
- // Sanitizers use their own ASLR mask constant.
- #if BUILDFLAG(IS_WIN) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
- if (!IsWindows8Point1OrGreater()) {
- mask = internal::ASLRMaskBefore8_10();
- }
- #endif // BUILDFLAG(IS_WIN) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR))
- #elif defined(ARCH_CPU_32_BITS)
- #if BUILDFLAG(IS_WIN)
- BOOL is_wow64 = FALSE;
- if (!IsWow64Process(GetCurrentProcess(), &is_wow64))
- is_wow64 = FALSE;
- if (!is_wow64) {
- mask = 0;
- }
- #endif // BUILDFLAG(IS_WIN)
- #endif // defined(ARCH_CPU_32_BITS)
- return mask;
- }
- const size_t kSamples = 100;
- uintptr_t GetAddressBits() {
- return GetRandomPageBase();
- }
- uintptr_t GetRandomBits() {
- return GetAddressBits() - internal::ASLROffset();
- }
- } // namespace
- // Configurations without ASLR are tested here.
- TEST(PartitionAllocAddressSpaceRandomizationTest, DisabledASLR) {
- uintptr_t mask = GetMask();
- if (!mask) {
- #if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_32_BITS)
- // ASLR should be turned off on 32-bit Windows.
- EXPECT_EQ(0u, GetRandomPageBase());
- #else
- // Otherwise, 0 is very unexpected.
- EXPECT_NE(0u, GetRandomPageBase());
- #endif
- }
- }
- TEST(PartitionAllocAddressSpaceRandomizationTest, Alignment) {
- uintptr_t mask = GetMask();
- if (!mask)
- return;
- for (size_t i = 0; i < kSamples; ++i) {
- uintptr_t address = GetAddressBits();
- EXPECT_EQ(0ULL,
- (address & internal::PageAllocationGranularityOffsetMask()));
- }
- }
- TEST(PartitionAllocAddressSpaceRandomizationTest, Range) {
- uintptr_t mask = GetMask();
- if (!mask)
- return;
- uintptr_t min = internal::ASLROffset();
- uintptr_t max = internal::ASLROffset() + internal::ASLRMask();
- for (size_t i = 0; i < kSamples; ++i) {
- uintptr_t address = GetAddressBits();
- EXPECT_LE(min, address);
- EXPECT_GE(max + mask, address);
- }
- }
- TEST(PartitionAllocAddressSpaceRandomizationTest, Predictable) {
- uintptr_t mask = GetMask();
- if (!mask)
- return;
- const uint64_t kInitialSeed = 0xfeed5eedULL;
- SetMmapSeedForTesting(kInitialSeed);
- std::vector<uintptr_t> sequence;
- for (size_t i = 0; i < kSamples; ++i) {
- sequence.push_back(GetRandomPageBase());
- }
- SetMmapSeedForTesting(kInitialSeed);
- for (size_t i = 0; i < kSamples; ++i) {
- EXPECT_EQ(GetRandomPageBase(), sequence[i]);
- }
- }
- // This randomness test is adapted from V8's PRNG tests.
- // Chi squared for getting m 0s out of n bits.
- double ChiSquared(int m, int n) {
- double ys_minus_np1 = (m - n / 2.0);
- double chi_squared_1 = ys_minus_np1 * ys_minus_np1 * 2.0 / n;
- double ys_minus_np2 = ((n - m) - n / 2.0);
- double chi_squared_2 = ys_minus_np2 * ys_minus_np2 * 2.0 / n;
- return chi_squared_1 + chi_squared_2;
- }
- // Test for correlations between recent bits from the PRNG, or bits that are
- // biased.
- void RandomBitCorrelation(int random_bit) {
- uintptr_t mask = GetMask();
- if ((mask & (1ULL << random_bit)) == 0)
- return; // bit is always 0.
- #if BUILDFLAG(PA_DCHECK_IS_ON)
- // Do fewer checks when BUILDFLAG(PA_DCHECK_IS_ON). Exercized code only
- // changes when the random number generator does, which should be almost
- // never. However it's expensive to run all the tests. So keep iterations
- // faster for local development builds, while having the stricter version run
- // on official build testers.
- constexpr int kHistory = 2;
- constexpr int kRepeats = 1000;
- #else
- constexpr int kHistory = 8;
- constexpr int kRepeats = 10000;
- #endif
- constexpr int kPointerBits = 8 * sizeof(void*);
- uintptr_t history[kHistory];
- // The predictor bit is either constant 0 or 1, or one of the bits from the
- // history.
- for (int predictor_bit = -2; predictor_bit < kPointerBits; predictor_bit++) {
- // The predicted bit is one of the bits from the PRNG.
- for (int ago = 0; ago < kHistory; ago++) {
- // We don't want to check whether each bit predicts itself.
- if (ago == 0 && predictor_bit == random_bit)
- continue;
- // Enter the new random value into the history.
- for (int i = ago; i >= 0; i--) {
- history[i] = GetRandomBits();
- }
- // Find out how many of the bits are the same as the prediction bit.
- int m = 0;
- for (int i = 0; i < kRepeats; i++) {
- uintptr_t random = GetRandomBits();
- for (int j = ago - 1; j >= 0; j--)
- history[j + 1] = history[j];
- history[0] = random;
- int predicted;
- if (predictor_bit >= 0) {
- predicted = (history[ago] >> predictor_bit) & 1;
- } else {
- predicted = predictor_bit == -2 ? 0 : 1;
- }
- int bit = (random >> random_bit) & 1;
- if (bit == predicted)
- m++;
- }
- // Chi squared analysis for k = 2 (2, states: same/not-same) and one
- // degree of freedom (k - 1).
- double chi_squared = ChiSquared(m, kRepeats);
- // For k=2 probability of Chi^2 < 35 is p=3.338e-9. This condition is
- // tested ~19000 times, so probability of it failing randomly per one
- // base_unittests run is (1 - (1 - p) ^ 19000) ~= 6e-5.
- PA_CHECK(chi_squared <= 35.0);
- // If the predictor bit is a fixed 0 or 1 then it makes no sense to
- // repeat the test with a different age.
- if (predictor_bit < 0)
- break;
- }
- }
- }
- // Tests are fairly slow, so give each random bit its own test.
- #define TEST_RANDOM_BIT(BIT) \
- TEST(PartitionAllocAddressSpaceRandomizationTest, \
- RandomBitCorrelations##BIT) { \
- RandomBitCorrelation(BIT); \
- }
- // The first 12 bits on all platforms are always 0.
- TEST_RANDOM_BIT(12)
- TEST_RANDOM_BIT(13)
- TEST_RANDOM_BIT(14)
- TEST_RANDOM_BIT(15)
- TEST_RANDOM_BIT(16)
- TEST_RANDOM_BIT(17)
- TEST_RANDOM_BIT(18)
- TEST_RANDOM_BIT(19)
- TEST_RANDOM_BIT(20)
- TEST_RANDOM_BIT(21)
- TEST_RANDOM_BIT(22)
- TEST_RANDOM_BIT(23)
- TEST_RANDOM_BIT(24)
- TEST_RANDOM_BIT(25)
- TEST_RANDOM_BIT(26)
- TEST_RANDOM_BIT(27)
- TEST_RANDOM_BIT(28)
- TEST_RANDOM_BIT(29)
- TEST_RANDOM_BIT(30)
- TEST_RANDOM_BIT(31)
- #if defined(ARCH_CPU_64_BITS)
- TEST_RANDOM_BIT(32)
- TEST_RANDOM_BIT(33)
- TEST_RANDOM_BIT(34)
- TEST_RANDOM_BIT(35)
- TEST_RANDOM_BIT(36)
- TEST_RANDOM_BIT(37)
- TEST_RANDOM_BIT(38)
- TEST_RANDOM_BIT(39)
- TEST_RANDOM_BIT(40)
- TEST_RANDOM_BIT(41)
- TEST_RANDOM_BIT(42)
- TEST_RANDOM_BIT(43)
- TEST_RANDOM_BIT(44)
- TEST_RANDOM_BIT(45)
- TEST_RANDOM_BIT(46)
- TEST_RANDOM_BIT(47)
- TEST_RANDOM_BIT(48)
- // No platforms have more than 48 address bits.
- #endif // defined(ARCH_CPU_64_BITS)
- #undef TEST_RANDOM_BIT
- // Checks that we can actually map memory in the requested range.
- // TODO(crbug.com/1318466): Extend to all operating systems once they are fixed.
- #if BUILDFLAG(IS_MAC)
- TEST(PartitionAllocAddressSpaceRandomizationTest, CanMapInAslrRange) {
- int tries = 0;
- // This is overly generous, but we really don't want to make the test flaky.
- constexpr int kMaxTries = 1000;
- for (tries = 0; tries < kMaxTries; tries++) {
- uintptr_t requested_address = GetRandomPageBase();
- size_t size = internal::PageAllocationGranularity();
- uintptr_t address = AllocPages(
- requested_address, size, internal::PageAllocationGranularity(),
- PageAccessibilityConfiguration::kReadWrite, PageTag::kPartitionAlloc);
- ASSERT_NE(address, 0u);
- FreePages(address, size);
- if (address == requested_address)
- break;
- }
- EXPECT_LT(tries, kMaxTries);
- }
- #endif // BUILDFLAG(IS_MAC)
- } // namespace partition_alloc
|