Commit 0ad0d051 authored by Bill Budge's avatar Bill Budge Committed by Commit Bot

[page_allocator] Support predictable ASLR calculation for fuzzers.

- Adds a 'initial_seed' parameter to GetRandomPageBase, which defaults
  to 0. If non-zero, generates a predictable sequence, using the value
  as the initial seed.
- Adds a unit test for the predictable sequence.

Bug: chromium:756050
Change-Id: Iaf718baa2b7d49dc8eda7b0f97aa12b576ddca28
Reviewed-on: https://chromium-review.googlesource.com/703509
Commit-Queue: Bill Budge <bbudge@chromium.org>
Reviewed-by: default avatarChris Palmer <palmer@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#509575}
parent 4b4984e7
...@@ -37,6 +37,8 @@ struct ranctx { ...@@ -37,6 +37,8 @@ struct ranctx {
uint32_t d; uint32_t d;
}; };
static LazyInstance<ranctx>::Leaky s_ranctx = LAZY_INSTANCE_INITIALIZER;
#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) #define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
uint32_t ranvalInternal(ranctx* x) { uint32_t ranvalInternal(ranctx* x) {
...@@ -81,12 +83,20 @@ uint32_t ranval(ranctx* x) { ...@@ -81,12 +83,20 @@ uint32_t ranval(ranctx* x) {
return ret; return ret;
} }
static LazyInstance<ranctx>::Leaky s_ranctx = LAZY_INSTANCE_INITIALIZER;
} // namespace } // namespace
// Calculates a random preferred mapping address. In calculating an address, we void SetRandomPageBaseSeed(int64_t seed) {
// balance good ASLR against not fragmenting the address space too badly. // This code must not be included in builds shipped to end users.
#if defined(UNSAFE_DEVELOPER_BUILD)
ranctx* x = s_ranctx.Pointer();
subtle::SpinLock::Guard guard(x->lock);
// Set RNG to initial state.
x->initialized = true;
x->a = x->b = static_cast<uint32_t>(seed);
x->c = x->d = static_cast<uint32_t>(seed >> 32);
#endif // !defined(UNSAFE_DEVELOPER_BUILD)
}
void* GetRandomPageBase() { void* GetRandomPageBase() {
uintptr_t random = static_cast<uintptr_t>(ranval(s_ranctx.Pointer())); uintptr_t random = static_cast<uintptr_t>(ranval(s_ranctx.Pointer()));
......
...@@ -11,6 +11,15 @@ ...@@ -11,6 +11,15 @@
namespace base { namespace base {
// Sets the seed for the random number generator used by GetRandomPageBase in
// order to generate a predictable sequence of addresses. May be called multiple
// times. On official Chrome builds this function is disabled and has no effect.
BASE_EXPORT void SetRandomPageBaseSeed(int64_t seed);
// Calculates a random preferred mapping address. In calculating an address, we
// balance good ASLR against not fragmenting the address space too badly.
BASE_EXPORT void* GetRandomPageBase();
namespace internal { namespace internal {
constexpr uintptr_t AslrAddress(uintptr_t mask) { constexpr uintptr_t AslrAddress(uintptr_t mask) {
...@@ -129,10 +138,6 @@ constexpr uintptr_t kASLROffset = AslrAddress(0x20000000ULL); ...@@ -129,10 +138,6 @@ constexpr uintptr_t kASLROffset = AslrAddress(0x20000000ULL);
} // namespace internal } // namespace internal
// Calculates a random preferred mapping address. In calculating an address, we
// balance good ASLR against not fragmenting the address space too badly.
BASE_EXPORT void* GetRandomPageBase();
} // namespace base } // namespace base
#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION #endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION
...@@ -20,7 +20,9 @@ ...@@ -20,7 +20,9 @@
namespace base { namespace base {
TEST(AddressSpaceRandomizationTest, GetRandomPageBase) { namespace {
uintptr_t GetMask() {
uintptr_t mask = internal::kASLRMask; uintptr_t mask = internal::kASLRMask;
#if defined(ARCH_CPU_64_BITS) #if defined(ARCH_CPU_64_BITS)
#if defined(OS_WIN) #if defined(OS_WIN)
...@@ -34,12 +36,28 @@ TEST(AddressSpaceRandomizationTest, GetRandomPageBase) { ...@@ -34,12 +36,28 @@ TEST(AddressSpaceRandomizationTest, GetRandomPageBase) {
if (!IsWow64Process(GetCurrentProcess(), &is_wow64)) if (!IsWow64Process(GetCurrentProcess(), &is_wow64))
is_wow64 = FALSE; is_wow64 = FALSE;
if (!is_wow64) { if (!is_wow64) {
// ASLR is turned off on 32-bit Windows; check that result is null. mask = 0;
EXPECT_EQ(nullptr, base::GetRandomPageBase());
return;
} }
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
#endif // defined(ARCH_CPU_32_BITS) #endif // defined(ARCH_CPU_32_BITS)
return mask;
}
} // namespace
TEST(AddressSpaceRandomizationTest, Unpredictable) {
uintptr_t mask = GetMask();
if (!mask) {
#if defined(OS_WIN) && defined(ARCH_CPU_32_BITS)
// ASLR should be turned off on 32-bit Windows.
EXPECT_EQ(nullptr, base::GetRandomPageBase());
#else
// Otherwise, nullptr is very unexpected.
EXPECT_NE(nullptr, base::GetRandomPageBase());
#endif
return;
}
// Sample the first 100 addresses. // Sample the first 100 addresses.
std::set<uintptr_t> addresses; std::set<uintptr_t> addresses;
uintptr_t address_logical_sum = 0; uintptr_t address_logical_sum = 0;
...@@ -68,4 +86,46 @@ TEST(AddressSpaceRandomizationTest, GetRandomPageBase) { ...@@ -68,4 +86,46 @@ TEST(AddressSpaceRandomizationTest, GetRandomPageBase) {
EXPECT_EQ(0ULL, address_logical_product & mask); EXPECT_EQ(0ULL, address_logical_product & mask);
} }
#if defined(UNSAFE_DEVELPER_BUILD)
TEST(AddressSpaceRandomizationTest, Predictable) {
const uintptr_t kInitialSeed = 0xfeed5eedULL;
base::SetRandomPageBaseSeed(kInitialSeed);
uintptr_t mask = GetMask();
if (!mask) {
#if defined(OS_WIN) && defined(ARCH_CPU_32_BITS)
// ASLR should be turned off on 32-bit Windows.
EXPECT_EQ(nullptr, base::GetRandomPageBase());
#else
// Otherwise, nullptr is very unexpected.
EXPECT_NE(nullptr, base::GetRandomPageBase());
#endif
return;
}
// The first 4 elements of the random sequences generated from kInitialSeed.
#if ARCH_CPU_32_BITS
const uint32_t random[4] = {0x8de352e3, 0xe6da7cd8, 0x9e7eb32d, 0xe8c2b6c};
#elif ARCH_CPU_64_BITS
const uint64_t random[4] = {0x8de352e3e6da7cd8, 0x9e7eb32d0e8c2b6c,
0xd3cc6055308d048d, 0xe229f78b344317a5};
#endif
// Make sure the addresses look random but are predictable.
std::set<uintptr_t> addresses;
for (int i = 0; i < 4; i++) {
uintptr_t address = reinterpret_cast<uintptr_t>(base::GetRandomPageBase());
// Test that address is in range.
EXPECT_LE(internal::kASLROffset, address);
EXPECT_GE(internal::kASLROffset + mask, address);
// Test that address is page aligned.
EXPECT_EQ(0ULL, (address & kPageAllocationGranularityOffsetMask));
// Test that address is unique (no collisions in 100 tries)
CHECK_EQ(0ULL, addresses.count(address));
addresses.insert(address);
// Test that (address - offset) == (predicted & mask).
address -= internal::kASLROffset;
EXPECT_EQ(random[i] & internal::kASLRMask, address);
}
}
#endif // defined(UNSAFE_DEVELPER_BUILD)
} // namespace base } // namespace base
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment