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> |
11 | 11 |
12 #include <algorithm> | 12 #include <algorithm> |
13 #include <limits> | 13 #include <limits> |
14 | 14 |
15 #include "base/file_util.h" | 15 #include "base/file_util.h" |
16 #include "base/logging.h" | 16 #include "base/logging.h" |
17 #include "base/memory/scoped_ptr.h" | 17 #include "base/memory/scoped_ptr.h" |
| 18 #include "build/build_config.h" |
18 #include "testing/gtest/include/gtest/gtest.h" | 19 #include "testing/gtest/include/gtest/gtest.h" |
19 | 20 |
| 21 #if defined(OS_POSIX) |
| 22 #include <sys/mman.h> |
| 23 #include <unistd.h> |
| 24 #endif |
| 25 |
20 using std::nothrow; | 26 using std::nothrow; |
21 using std::numeric_limits; | 27 using std::numeric_limits; |
22 | 28 |
23 namespace { | 29 namespace { |
24 | 30 |
25 // Check that we can not allocate a memory range that cannot be indexed | 31 // Check that we can not allocate a memory range that cannot be indexed |
26 // via an int. This is used to mitigate vulnerabilities in libraries that use | 32 // via an int. This is used to mitigate vulnerabilities in libraries that use |
27 // int instead of size_t. | 33 // int instead of size_t. |
28 // See crbug.com/169327. | 34 // See crbug.com/169327. |
29 | 35 |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
203 file_util::ScopedFD fd_closer(&fd); | 209 file_util::ScopedFD fd_closer(&fd); |
204 ASSERT_GE(fd, 0); | 210 ASSERT_GE(fd, 0); |
205 char buffer[1<<13]; | 211 char buffer[1<<13]; |
206 int ret; | 212 int ret; |
207 ret = read(fd, buffer, sizeof(buffer) - 1); | 213 ret = read(fd, buffer, sizeof(buffer) - 1); |
208 ASSERT_GT(ret, 0); | 214 ASSERT_GT(ret, 0); |
209 buffer[ret - 1] = 0; | 215 buffer[ret - 1] = 0; |
210 fprintf(stdout, "%s\n", buffer); | 216 fprintf(stdout, "%s\n", buffer); |
211 } | 217 } |
212 | 218 |
| 219 // Check if ptr1 and ptr2 are separated by less than size chars. |
| 220 bool ArePointersToSameArea(void* ptr1, void* ptr2, size_t size) { |
| 221 ptrdiff_t ptr_diff = reinterpret_cast<char*>(std::max(ptr1, ptr2)) - |
| 222 reinterpret_cast<char*>(std::min(ptr1, ptr2)); |
| 223 return static_cast<size_t>(ptr_diff) <= size; |
| 224 } |
| 225 |
213 // Check if TCMalloc uses an underlying random memory allocator. | 226 // Check if TCMalloc uses an underlying random memory allocator. |
214 TEST(SecurityTest, ALLOC_TEST(RandomMemoryAllocations)) { | 227 TEST(SecurityTest, ALLOC_TEST(RandomMemoryAllocations)) { |
215 if (IsTcMallocBypassed()) | 228 if (IsTcMallocBypassed()) |
216 return; | 229 return; |
217 // Two successsive calls to mmap() have roughly one chance out of 2^6 to | 230 size_t kPageSize = 4096; // We support x86_64 only. |
218 // have the same two high order nibbles, which is what we are looking at in | 231 // Check that malloc() returns an address that is neither the kernel's |
219 // this test. (In the implementation, we mask these two nibbles with 0x3f, | 232 // un-hinted mmap area, nor the current brk() area. The first malloc() may |
220 // hence the 6 bits). | 233 // not be at a random address because TCMalloc will first exhaust any memory |
221 // With 32 allocations, we see ~16 that end-up in different buckets (i.e. | 234 // that it has allocated early on, before starting the sophisticated |
222 // zones mapped via mmap(), so the chances of this test flaking is roughly | 235 // allocators. |
223 // 2^-(6*15). | 236 void* default_mmap_heap_address = |
224 const int kAllocNumber = 32; | 237 mmap(0, kPageSize, PROT_READ|PROT_WRITE, |
225 // Make kAllocNumber successive allocations of growing size and compare the | 238 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); |
226 // successive pointers to detect adjacent mappings. We grow the size because | 239 ASSERT_NE(default_mmap_heap_address, |
227 // TCMalloc can sometimes over-allocate. | 240 static_cast<void*>(MAP_FAILED)); |
228 scoped_ptr<char, base::FreeDeleter> ptr[kAllocNumber]; | 241 ASSERT_EQ(munmap(default_mmap_heap_address, kPageSize), 0); |
229 for (int i = 0; i < kAllocNumber; ++i) { | 242 void* brk_heap_address = sbrk(0); |
230 // Grow the Malloc size slightly sub-exponentially. | 243 ASSERT_NE(brk_heap_address, reinterpret_cast<void*>(-1)); |
231 const size_t kMallocSize = 1 << (12 + (i>>1)); | 244 ASSERT_TRUE(brk_heap_address != NULL); |
232 ptr[i].reset(static_cast<char*>(malloc(kMallocSize))); | 245 // 1 MB should get us past what TCMalloc pre-allocated before initializing |
233 ASSERT_TRUE(ptr[i] != NULL); | 246 // the sophisticated allocators. |
234 if (i > 0) { | 247 size_t kAllocSize = 1<<20; |
235 // Without mmap randomization, the two high order nibbles | 248 scoped_ptr<char, base::FreeDeleter> ptr( |
236 // of a 47 bits userland address address will be identical. | 249 static_cast<char*>(malloc(kAllocSize))); |
237 // We're only watching the 6 bits that we actually do touch | 250 ASSERT_TRUE(ptr != NULL); |
238 // in our implementation. | 251 // If two pointers are separated by less than 512MB, they are considered |
239 const uintptr_t kHighOrderMask = 0x3f0000000000ULL; | 252 // to be in the same area. |
240 bool pointer_have_same_high_order = | 253 // Our random pointer could be anywhere within 0x3fffffffffff (46bits), |
241 (reinterpret_cast<size_t>(ptr[i].get()) & kHighOrderMask) == | 254 // and we are checking that it's not withing 1GB (30 bits) from two |
242 (reinterpret_cast<size_t>(ptr[i - 1].get()) & kHighOrderMask); | 255 // addresses (brk and mmap heap). We have roughly one chance out of |
243 if (!pointer_have_same_high_order) { | 256 // 2^15 to flake. |
244 // PrintProcSelfMaps(); | 257 const size_t kAreaRadius = 1<<29; |
245 return; // Test passes. | 258 bool in_default_mmap_heap = ArePointersToSameArea( |
246 } | 259 ptr.get(), default_mmap_heap_address, kAreaRadius); |
247 } | 260 EXPECT_FALSE(in_default_mmap_heap); |
248 } | 261 |
249 ASSERT_TRUE(false); // NOTREACHED(); | 262 bool in_default_brk_heap = ArePointersToSameArea( |
| 263 ptr.get(), brk_heap_address, kAreaRadius); |
| 264 EXPECT_FALSE(in_default_brk_heap); |
| 265 |
| 266 // In the implementation, we always mask our random addresses with |
| 267 // kRandomMask, so we use it as an additional detection mechanism. |
| 268 const uintptr_t kRandomMask = 0x3fffffffffffULL; |
| 269 bool impossible_random_address = |
| 270 reinterpret_cast<uintptr_t>(ptr.get()) & ~kRandomMask; |
| 271 EXPECT_FALSE(impossible_random_address); |
250 } | 272 } |
251 | 273 |
252 #endif // (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(__x86_64__) | 274 #endif // (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(__x86_64__) |
253 | 275 |
254 } // namespace | 276 } // namespace |
OLD | NEW |