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 |