| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <fcntl.h> | 5 #include <fcntl.h> |
| 6 #include <stdio.h> | 6 #include <stdio.h> |
| 7 #include <stdlib.h> | 7 #include <stdlib.h> |
| 8 #include <string.h> | 8 #include <string.h> |
| 9 #include <sys/stat.h> | 9 #include <sys/stat.h> |
| 10 #include <sys/types.h> | 10 #include <sys/types.h> |
| (...skipping 19 matching lines...) Expand all Loading... |
| 30 | 30 |
| 31 // This function acts as a compiler optimization barrier. We use it to | 31 // This function acts as a compiler optimization barrier. We use it to |
| 32 // prevent the compiler from making an expression a compile-time constant. | 32 // prevent the compiler from making an expression a compile-time constant. |
| 33 // We also use it so that the compiler doesn't discard certain return values | 33 // We also use it so that the compiler doesn't discard certain return values |
| 34 // as something we don't need (see the comment with calloc below). | 34 // as something we don't need (see the comment with calloc below). |
| 35 template <typename Type> | 35 template <typename Type> |
| 36 Type HideValueFromCompiler(volatile Type value) { | 36 Type HideValueFromCompiler(volatile Type value) { |
| 37 return value; | 37 return value; |
| 38 } | 38 } |
| 39 | 39 |
| 40 // Check that we can not allocate a memory range that cannot be indexed | 40 // - NO_TCMALLOC (should be defined if we compile with linux_use_tcmalloc=0) |
| 41 // via an int. This is used to mitigate vulnerabilities in libraries that use | |
| 42 // int instead of size_t. | |
| 43 // See crbug.com/169327. | |
| 44 | |
| 45 // - NO_TCMALLOC because we only patched tcmalloc | |
| 46 // - ADDRESS_SANITIZER because it has its own memory allocator | 41 // - ADDRESS_SANITIZER because it has its own memory allocator |
| 47 // - IOS does not seem to honor nothrow in new properly | 42 // - IOS does not use tcmalloc |
| 48 // - OS_MACOSX does not use tcmalloc | 43 // - OS_MACOSX does not use tcmalloc |
| 49 #if !defined(NO_TCMALLOC) && !defined(ADDRESS_SANITIZER) && \ | 44 #if !defined(NO_TCMALLOC) && !defined(ADDRESS_SANITIZER) && \ |
| 50 !defined(OS_IOS) && !defined(OS_MACOSX) | 45 !defined(OS_IOS) && !defined(OS_MACOSX) |
| 51 #define ALLOC_TEST(function) function | 46 #define TCMALLOC_TEST(function) function |
| 52 #else | 47 #else |
| 53 #define ALLOC_TEST(function) DISABLED_##function | 48 #define TCMALLOC_TEST(function) DISABLED_##function |
| 54 #endif | 49 #endif |
| 55 | 50 |
| 56 // TODO(jln): switch to std::numeric_limits<int>::max() when we switch to | 51 // TODO(jln): switch to std::numeric_limits<int>::max() when we switch to |
| 57 // C++11. | 52 // C++11. |
| 58 const size_t kTooBigAllocSize = INT_MAX; | 53 const size_t kTooBigAllocSize = INT_MAX; |
| 59 | 54 |
| 60 // Detect runtime TCMalloc bypasses. | 55 // Detect runtime TCMalloc bypasses. |
| 61 bool IsTcMallocBypassed() { | 56 bool IsTcMallocBypassed() { |
| 62 #if defined(OS_LINUX) || defined(OS_CHROMEOS) | 57 #if defined(OS_LINUX) || defined(OS_CHROMEOS) |
| 63 // This should detect a TCMalloc bypass from Valgrind. | 58 // This should detect a TCMalloc bypass from Valgrind. |
| 64 char* g_slice = getenv("G_SLICE"); | 59 char* g_slice = getenv("G_SLICE"); |
| 65 if (g_slice && !strcmp(g_slice, "always-malloc")) | 60 if (g_slice && !strcmp(g_slice, "always-malloc")) |
| 66 return true; | 61 return true; |
| 67 #endif | 62 #endif |
| 68 return false; | 63 return false; |
| 69 } | 64 } |
| 70 | 65 |
| 66 bool CallocDiesOnOOM() { |
| 67 // The wrapper function in base/process_util_linux.cc that is used when we |
| 68 // compile without TCMalloc will just die on OOM instead of returning NULL. |
| 69 #if defined(OS_LINUX) && defined(NO_TCMALLOC) |
| 70 return true; |
| 71 #else |
| 72 return false; |
| 73 #endif |
| 74 } |
| 75 |
| 71 // Fake test that allow to know the state of TCMalloc by looking at bots. | 76 // Fake test that allow to know the state of TCMalloc by looking at bots. |
| 72 TEST(SecurityTest, ALLOC_TEST(IsTCMallocDynamicallyBypassed)) { | 77 TEST(SecurityTest, TCMALLOC_TEST(IsTCMallocDynamicallyBypassed)) { |
| 73 printf("Malloc is dynamically bypassed: %s\n", | 78 printf("Malloc is dynamically bypassed: %s\n", |
| 74 IsTcMallocBypassed() ? "yes." : "no."); | 79 IsTcMallocBypassed() ? "yes." : "no."); |
| 75 } | 80 } |
| 76 | 81 |
| 77 TEST(SecurityTest, ALLOC_TEST(MemoryAllocationRestrictionsMalloc)) { | 82 // The MemoryAllocationRestrictions* tests test that we can not allocate a |
| 83 // memory range that cannot be indexed via an int. This is used to mitigate |
| 84 // vulnerabilities in libraries that use int instead of size_t. See |
| 85 // crbug.com/169327. |
| 86 |
| 87 TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsMalloc)) { |
| 78 if (!IsTcMallocBypassed()) { | 88 if (!IsTcMallocBypassed()) { |
| 79 scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>( | 89 scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>( |
| 80 HideValueFromCompiler(malloc(kTooBigAllocSize)))); | 90 HideValueFromCompiler(malloc(kTooBigAllocSize)))); |
| 81 ASSERT_TRUE(!ptr); | 91 ASSERT_TRUE(!ptr); |
| 82 } | 92 } |
| 83 } | 93 } |
| 84 | 94 |
| 85 TEST(SecurityTest, ALLOC_TEST(MemoryAllocationRestrictionsCalloc)) { | 95 TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsCalloc)) { |
| 86 if (!IsTcMallocBypassed()) { | 96 if (!IsTcMallocBypassed()) { |
| 87 scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>( | 97 scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>( |
| 88 HideValueFromCompiler(calloc(kTooBigAllocSize, 1)))); | 98 HideValueFromCompiler(calloc(kTooBigAllocSize, 1)))); |
| 89 ASSERT_TRUE(!ptr); | 99 ASSERT_TRUE(!ptr); |
| 90 } | 100 } |
| 91 } | 101 } |
| 92 | 102 |
| 93 TEST(SecurityTest, ALLOC_TEST(MemoryAllocationRestrictionsRealloc)) { | 103 TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsRealloc)) { |
| 94 if (!IsTcMallocBypassed()) { | 104 if (!IsTcMallocBypassed()) { |
| 95 char* orig_ptr = static_cast<char*>(malloc(1)); | 105 char* orig_ptr = static_cast<char*>(malloc(1)); |
| 96 ASSERT_TRUE(orig_ptr); | 106 ASSERT_TRUE(orig_ptr); |
| 97 scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>( | 107 scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>( |
| 98 HideValueFromCompiler(realloc(orig_ptr, kTooBigAllocSize)))); | 108 HideValueFromCompiler(realloc(orig_ptr, kTooBigAllocSize)))); |
| 99 ASSERT_TRUE(!ptr); | 109 ASSERT_TRUE(!ptr); |
| 100 // If realloc() did not succeed, we need to free orig_ptr. | 110 // If realloc() did not succeed, we need to free orig_ptr. |
| 101 free(orig_ptr); | 111 free(orig_ptr); |
| 102 } | 112 } |
| 103 } | 113 } |
| 104 | 114 |
| 105 typedef struct { | 115 typedef struct { |
| 106 char large_array[kTooBigAllocSize]; | 116 char large_array[kTooBigAllocSize]; |
| 107 } VeryLargeStruct; | 117 } VeryLargeStruct; |
| 108 | 118 |
| 109 TEST(SecurityTest, ALLOC_TEST(MemoryAllocationRestrictionsNew)) { | 119 TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsNew)) { |
| 110 if (!IsTcMallocBypassed()) { | 120 if (!IsTcMallocBypassed()) { |
| 111 scoped_ptr<VeryLargeStruct> ptr( | 121 scoped_ptr<VeryLargeStruct> ptr( |
| 112 HideValueFromCompiler(new (nothrow) VeryLargeStruct)); | 122 HideValueFromCompiler(new (nothrow) VeryLargeStruct)); |
| 113 ASSERT_TRUE(!ptr); | 123 ASSERT_TRUE(!ptr); |
| 114 } | 124 } |
| 115 } | 125 } |
| 116 | 126 |
| 117 TEST(SecurityTest, ALLOC_TEST(MemoryAllocationRestrictionsNewArray)) { | 127 TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsNewArray)) { |
| 118 if (!IsTcMallocBypassed()) { | 128 if (!IsTcMallocBypassed()) { |
| 119 scoped_ptr<char[]> ptr( | 129 scoped_ptr<char[]> ptr( |
| 120 HideValueFromCompiler(new (nothrow) char[kTooBigAllocSize])); | 130 HideValueFromCompiler(new (nothrow) char[kTooBigAllocSize])); |
| 121 ASSERT_TRUE(!ptr); | 131 ASSERT_TRUE(!ptr); |
| 122 } | 132 } |
| 123 } | 133 } |
| 124 | 134 |
| 125 // The tests bellow check for overflows in new[] and calloc(). | 135 // The tests bellow check for overflows in new[] and calloc(). |
| 126 | 136 |
| 127 #if defined(OS_IOS) || defined(OS_WIN) | 137 #if defined(OS_IOS) || defined(OS_WIN) |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 177 OverflowTestsSoftExpectTrue(!array_pointer); | 187 OverflowTestsSoftExpectTrue(!array_pointer); |
| 178 } | 188 } |
| 179 { | 189 { |
| 180 scoped_ptr<char[][kArraySize2]> array_pointer(new (nothrow) | 190 scoped_ptr<char[][kArraySize2]> array_pointer(new (nothrow) |
| 181 char[kDynamicArraySize][kArraySize2]); | 191 char[kDynamicArraySize][kArraySize2]); |
| 182 OverflowTestsSoftExpectTrue(!array_pointer); | 192 OverflowTestsSoftExpectTrue(!array_pointer); |
| 183 } | 193 } |
| 184 } | 194 } |
| 185 #endif | 195 #endif |
| 186 | 196 |
| 197 // Call calloc(), eventually free the memory and return whether or not |
| 198 // calloc() did succeed. |
| 199 bool CallocReturnsNull(size_t nmemb, size_t size) { |
| 200 scoped_ptr<char, base::FreeDeleter> array_pointer( |
| 201 static_cast<char*>(calloc(nmemb, size))); |
| 202 // We need the call to HideValueFromCompiler(): we have seen LLVM |
| 203 // optimize away the call to calloc() entirely and assume |
| 204 // the pointer to not be NULL. |
| 205 return HideValueFromCompiler(array_pointer.get()) == NULL; |
| 206 } |
| 207 |
| 187 // Test if calloc() can overflow. Disable on ASAN for now since the | 208 // Test if calloc() can overflow. Disable on ASAN for now since the |
| 188 // overflow seems present there. | 209 // overflow seems present there (crbug.com/175554). |
| 189 TEST(SecurityTest, DISABLE_ON_ASAN(CallocOverflow)) { | 210 TEST(SecurityTest, DISABLE_ON_ASAN(CallocOverflow)) { |
| 190 const size_t kArraySize = 4096; | 211 const size_t kArraySize = 4096; |
| 191 const size_t kMaxSizeT = numeric_limits<size_t>::max(); | 212 const size_t kMaxSizeT = numeric_limits<size_t>::max(); |
| 192 const size_t kArraySize2 = kMaxSizeT / kArraySize + 10; | 213 const size_t kArraySize2 = kMaxSizeT / kArraySize + 10; |
| 193 { | 214 if (!CallocDiesOnOOM()) { |
| 194 scoped_ptr<char> array_pointer( | 215 EXPECT_TRUE(CallocReturnsNull(kArraySize, kArraySize2)); |
| 195 static_cast<char*>(calloc(kArraySize, kArraySize2))); | 216 EXPECT_TRUE(CallocReturnsNull(kArraySize2, kArraySize)); |
| 196 // We need the call to HideValueFromCompiler(): we have seen LLVM | 217 } else { |
| 197 // optimize away the call to calloc() entirely and assume | 218 // It's also ok for calloc to just terminate the process. |
| 198 // the pointer to not be NULL. | 219 #if defined(GTEST_HAS_DEATH_TEST) |
| 199 EXPECT_TRUE(HideValueFromCompiler(array_pointer.get()) == NULL); | 220 EXPECT_DEATH(CallocReturnsNull(kArraySize, kArraySize2), ""); |
| 200 } | 221 EXPECT_DEATH(CallocReturnsNull(kArraySize2, kArraySize), ""); |
| 201 { | 222 #endif // GTEST_HAS_DEATH_TEST |
| 202 scoped_ptr<char> array_pointer( | |
| 203 static_cast<char*>(calloc(kArraySize2, kArraySize))); | |
| 204 // We need the call to HideValueFromCompiler(): we have seen LLVM | |
| 205 // optimize away the call to calloc() entirely and assume | |
| 206 // the pointer to not be NULL. | |
| 207 EXPECT_TRUE(HideValueFromCompiler(array_pointer.get()) == NULL); | |
| 208 } | 223 } |
| 209 } | 224 } |
| 210 | 225 |
| 211 #if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(__x86_64__) | 226 #if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(__x86_64__) |
| 212 // Useful for debugging. | 227 // Useful for debugging. |
| 213 void PrintProcSelfMaps() { | 228 void PrintProcSelfMaps() { |
| 214 int fd = open("/proc/self/maps", O_RDONLY); | 229 int fd = open("/proc/self/maps", O_RDONLY); |
| 215 file_util::ScopedFD fd_closer(&fd); | 230 file_util::ScopedFD fd_closer(&fd); |
| 216 ASSERT_GE(fd, 0); | 231 ASSERT_GE(fd, 0); |
| 217 char buffer[1<<13]; | 232 char buffer[1<<13]; |
| 218 int ret; | 233 int ret; |
| 219 ret = read(fd, buffer, sizeof(buffer) - 1); | 234 ret = read(fd, buffer, sizeof(buffer) - 1); |
| 220 ASSERT_GT(ret, 0); | 235 ASSERT_GT(ret, 0); |
| 221 buffer[ret - 1] = 0; | 236 buffer[ret - 1] = 0; |
| 222 fprintf(stdout, "%s\n", buffer); | 237 fprintf(stdout, "%s\n", buffer); |
| 223 } | 238 } |
| 224 | 239 |
| 225 // Check if ptr1 and ptr2 are separated by less than size chars. | 240 // Check if ptr1 and ptr2 are separated by less than size chars. |
| 226 bool ArePointersToSameArea(void* ptr1, void* ptr2, size_t size) { | 241 bool ArePointersToSameArea(void* ptr1, void* ptr2, size_t size) { |
| 227 ptrdiff_t ptr_diff = reinterpret_cast<char*>(std::max(ptr1, ptr2)) - | 242 ptrdiff_t ptr_diff = reinterpret_cast<char*>(std::max(ptr1, ptr2)) - |
| 228 reinterpret_cast<char*>(std::min(ptr1, ptr2)); | 243 reinterpret_cast<char*>(std::min(ptr1, ptr2)); |
| 229 return static_cast<size_t>(ptr_diff) <= size; | 244 return static_cast<size_t>(ptr_diff) <= size; |
| 230 } | 245 } |
| 231 | 246 |
| 232 // Check if TCMalloc uses an underlying random memory allocator. | 247 // Check if TCMalloc uses an underlying random memory allocator. |
| 233 TEST(SecurityTest, ALLOC_TEST(RandomMemoryAllocations)) { | 248 TEST(SecurityTest, TCMALLOC_TEST(RandomMemoryAllocations)) { |
| 234 if (IsTcMallocBypassed()) | 249 if (IsTcMallocBypassed()) |
| 235 return; | 250 return; |
| 236 size_t kPageSize = 4096; // We support x86_64 only. | 251 size_t kPageSize = 4096; // We support x86_64 only. |
| 237 // Check that malloc() returns an address that is neither the kernel's | 252 // Check that malloc() returns an address that is neither the kernel's |
| 238 // un-hinted mmap area, nor the current brk() area. The first malloc() may | 253 // un-hinted mmap area, nor the current brk() area. The first malloc() may |
| 239 // not be at a random address because TCMalloc will first exhaust any memory | 254 // not be at a random address because TCMalloc will first exhaust any memory |
| 240 // that it has allocated early on, before starting the sophisticated | 255 // that it has allocated early on, before starting the sophisticated |
| 241 // allocators. | 256 // allocators. |
| 242 void* default_mmap_heap_address = | 257 void* default_mmap_heap_address = |
| 243 mmap(0, kPageSize, PROT_READ|PROT_WRITE, | 258 mmap(0, kPageSize, PROT_READ|PROT_WRITE, |
| (...skipping 29 matching lines...) Expand all Loading... |
| 273 // kRandomMask, so we use it as an additional detection mechanism. | 288 // kRandomMask, so we use it as an additional detection mechanism. |
| 274 const uintptr_t kRandomMask = 0x3fffffffffffULL; | 289 const uintptr_t kRandomMask = 0x3fffffffffffULL; |
| 275 bool impossible_random_address = | 290 bool impossible_random_address = |
| 276 reinterpret_cast<uintptr_t>(ptr.get()) & ~kRandomMask; | 291 reinterpret_cast<uintptr_t>(ptr.get()) & ~kRandomMask; |
| 277 EXPECT_FALSE(impossible_random_address); | 292 EXPECT_FALSE(impossible_random_address); |
| 278 } | 293 } |
| 279 | 294 |
| 280 #endif // (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(__x86_64__) | 295 #endif // (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(__x86_64__) |
| 281 | 296 |
| 282 } // namespace | 297 } // namespace |
| OLD | NEW |