Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(135)

Side by Side Diff: base/security_unittest.cc

Issue 12220107: Base: account for calloc aborting in security unittests (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Test for GTEST_HAS_DEATH_TEST. Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698