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 |