Chromium Code Reviews| Index: base/security_unittest.cc |
| diff --git a/base/security_unittest.cc b/base/security_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8c2f0ea2a4b16bfc20abb195555fccf2ee313557 |
| --- /dev/null |
| +++ b/base/security_unittest.cc |
| @@ -0,0 +1,135 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
|
Chris Evans
2013/01/15 01:53:08
2013
jln (very slow on Chromium)
2013/01/15 02:00:51
Done.
|
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include <stdio.h> |
| +#include <stdlib.h> |
| +#include <string.h> |
| + |
| +#include <algorithm> |
| +#include <limits> |
| + |
| +#include "base/logging.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +using std::nothrow; |
| + |
| +namespace { |
| + |
| +// Check that we can not allocate a memory range that cannot be indexed |
| +// via an int. This is used to mitigate vulnerabilities in libraries that use |
| +// int instead of size_t. |
| +// See crbug.com/169327. |
| + |
| +// - NO_TCMALLOC because we only patched tcmalloc |
| +// - ADDRESS_SANITIZER because it has its own memory allocator |
| +// - IOS does not seem to honor nothrow in new properly |
| +// - OS_MACOSX does not use tcmalloc |
| +#if !defined(NO_TCMALLOC) && !defined(ADDRESS_SANITIZER) && \ |
| + !defined(OS_IOS) && !defined(OS_MACOSX) |
| + #define ALLOC_TEST(function) function |
| +#else |
| + #define ALLOC_TEST(function) DISABLED_##function |
| +#endif |
| + |
| +// TODO(jln): switch to std::numeric_limits<int>::max() when we switch to |
| +// C++11. |
| +const size_t kMaxAllocSize = INT_MAX; |
| + |
| +// Detect runtime TCMalloc bypasses. |
| +bool IsTcMallocBypassed() { |
| +#if defined(OS_LINUX) |
| + // This should detect a TCMalloc bypass from Valgrind. |
| + char* g_slice = getenv("G_SLICE"); |
| + if (g_slice && !strcmp(g_slice, "always-malloc")) |
| + return true; |
| +#endif |
| + return false; |
| +} |
| + |
| +// Fake test that allow to know the state of TCMalloc by looking at bots. |
| +TEST(SecurityTest, ALLOC_TEST(IsTCMallocDynamicallyBypassed)) { |
| + printf("Malloc is dynamically bypassed: %s\n", |
| + IsTcMallocBypassed() ? "yes." : "no."); |
| +} |
| + |
| +TEST(SecurityTest, ALLOC_TEST(MemoryAllocationRestrictionsMalloc)) { |
| + if (!IsTcMallocBypassed()) { |
| + scoped_ptr<char, base::FreeDeleter> |
| + ptr(static_cast<char*>(malloc(kMaxAllocSize))); |
|
Chris Evans
2013/01/15 01:53:08
It's weird to have a test that "kMaxAllocSize" act
jln (very slow on Chromium)
2013/01/15 02:00:51
Good point, done.
|
| + ASSERT_TRUE(ptr == NULL); |
|
Chris Evans
2013/01/15 01:53:08
Indentation.
Chris Evans
2013/01/15 01:53:08
Indentation
jln (very slow on Chromium)
2013/01/15 02:00:51
Done.
|
| + } |
| +} |
| + |
| +TEST(SecurityTest, ALLOC_TEST(MemoryAllocationRestrictionsCalloc)) { |
| + if (!IsTcMallocBypassed()) { |
| + scoped_ptr<char, base::FreeDeleter> |
| + ptr(static_cast<char*>(calloc(kMaxAllocSize, 1))); |
| + ASSERT_TRUE(ptr == NULL); |
| + } |
| +} |
| + |
| +TEST(SecurityTest, ALLOC_TEST(MemoryAllocationRestrictionsRealloc)) { |
| + if (!IsTcMallocBypassed()) { |
| + char* orig_ptr = static_cast<char*>(malloc(1)); |
| + ASSERT_TRUE(orig_ptr != NULL); |
| + scoped_ptr<char, base::FreeDeleter> |
| + ptr(static_cast<char*>(realloc(orig_ptr, kMaxAllocSize))); |
| + ASSERT_TRUE(ptr == NULL); |
| + // If realloc() did not succeed, we need to free orig_ptr. |
| + free(orig_ptr); |
| + } |
| +} |
| + |
| +typedef struct { |
| + char large_array[kMaxAllocSize]; |
| +} VeryLargeStruct; |
| + |
| +TEST(SecurityTest, ALLOC_TEST(MemoryAllocationRestrictionsNew)) { |
| + if (!IsTcMallocBypassed()) { |
| + scoped_ptr<VeryLargeStruct> ptr(new (nothrow) VeryLargeStruct); |
| + ASSERT_TRUE(ptr == NULL); |
| + } |
| +} |
| + |
| +TEST(SecurityTest, ALLOC_TEST(MemoryAllocationRestrictionsNewArray)) { |
| + if (!IsTcMallocBypassed()) { |
| + scoped_array<char> ptr(new (nothrow) char[kMaxAllocSize]); |
| + ASSERT_TRUE(ptr == NULL); |
| + } |
| +} |
| + |
| +// This would be a test for a much stricter restriction preventing a contiguous |
| +// memory area of size MaximumAllocSize to be allocated via malloc. |
| +// Implementing this requires more effort and the test is disabled. |
|
Chris Evans
2013/01/15 01:53:08
I think we're better off not landing to be honest?
jln (very slow on Chromium)
2013/01/15 02:00:51
Done.
|
| +TEST(SecurityTest, DISABLED_MemoryAllocationRestrictionsStepped) { |
| + const int kAllocNumber = 8; |
| + const size_t kSteppedAllocSize = kMaxAllocSize / kAllocNumber; |
| + // Allow 1/32th for MetaData and padding. |
| + const size_t kExpectedExtra = kSteppedAllocSize >> 5; |
| + |
| + bool is_contiguous = true; |
| + // Make kAllocNumber successive allocations and compare the successive |
| + // pointers to detect adjacent mappings. |
| + scoped_ptr<char, base::FreeDeleter> ptr[kAllocNumber]; |
| + for (int i = 0; i < kAllocNumber; ++i) { |
| + ptr[i].reset(static_cast<char*>(malloc(kSteppedAllocSize))); |
| + if (ptr[i] == NULL) { |
| + is_contiguous = false; |
| + break; |
| + } |
| + if (i > 0) { |
| + size_t pointer_diff = static_cast<size_t>( |
| + std::max(ptr[i].get(), ptr[i - 1].get()) - |
| + std::min(ptr[i].get(), ptr[i - 1].get())); |
| + if (pointer_diff > (kSteppedAllocSize + kExpectedExtra)) { |
| + is_contiguous = false; |
| + break; |
| + } |
| + } |
| + } |
| + ASSERT_FALSE(is_contiguous); |
| +} |
| + |
| +} // namespace |