[go: nahoru, domu]

blob: 0ecaec415412e9a3880203ce8371846bb150aa98 [file] [log] [blame]
jln@chromium.orgb5bf9a132013-01-15 20:16:331// 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
3// found in the LICENSE file.
4
jln@chromium.org1b556f82013-01-31 02:23:435#include <fcntl.h>
jln@chromium.orgb5bf9a132013-01-15 20:16:336#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
jln@chromium.org1b556f82013-01-31 02:23:439#include <sys/stat.h>
10#include <sys/types.h>
jln@chromium.orgb5bf9a132013-01-15 20:16:3311
12#include <algorithm>
13#include <limits>
14
brettw@chromium.orge3177dd52014-08-13 20:22:1415#include "base/files/file_util.h"
jln@chromium.orgb5bf9a132013-01-15 20:16:3316#include "base/logging.h"
17#include "base/memory/scoped_ptr.h"
jln@chromium.org547683f2013-02-04 23:39:4818#include "build/build_config.h"
jln@chromium.orgb5bf9a132013-01-15 20:16:3319#include "testing/gtest/include/gtest/gtest.h"
jln@chromium.org547683f2013-02-04 23:39:4820
21#if defined(OS_POSIX)
22#include <sys/mman.h>
23#include <unistd.h>
24#endif
jln@chromium.orgb5bf9a132013-01-15 20:16:3325
26using std::nothrow;
jln@chromium.org9c4729b2013-01-26 04:41:1527using std::numeric_limits;
jln@chromium.orgb5bf9a132013-01-15 20:16:3328
29namespace {
30
jln@chromium.orgfe394f32013-02-06 03:23:4931// This function acts as a compiler optimization barrier. We use it to
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
34// as something we don't need (see the comment with calloc below).
35template <typename Type>
36Type HideValueFromCompiler(volatile Type value) {
jln@chromium.org1cdfdb72013-04-04 12:02:3537#if defined(__GNUC__)
38 // In a GCC compatible compiler (GCC or Clang), make this compiler barrier
39 // more robust than merely using "volatile".
40 __asm__ volatile ("" : "+r" (value));
41#endif // __GNUC__
jln@chromium.orgfe394f32013-02-06 03:23:4942 return value;
43}
44
dmikurube@chromium.org5fcce3f72014-03-05 20:07:2645// - NO_TCMALLOC (should be defined if compiled with use_allocator!="tcmalloc")
chrisha@google.comaee2f332014-03-27 15:08:0446// - ADDRESS_SANITIZER and SYZYASAN because they have their own memory allocator
jln@chromium.orgd6a6cea2013-02-15 06:10:4047// - IOS does not use tcmalloc
jln@chromium.orgb5bf9a132013-01-15 20:16:3348// - OS_MACOSX does not use tcmalloc
49#if !defined(NO_TCMALLOC) && !defined(ADDRESS_SANITIZER) && \
chrisha@google.comaee2f332014-03-27 15:08:0450 !defined(OS_IOS) && !defined(OS_MACOSX) && !defined(SYZYASAN)
jln@chromium.orgd6a6cea2013-02-15 06:10:4051 #define TCMALLOC_TEST(function) function
jln@chromium.orgb5bf9a132013-01-15 20:16:3352#else
jln@chromium.orgd6a6cea2013-02-15 06:10:4053 #define TCMALLOC_TEST(function) DISABLED_##function
jln@chromium.orgb5bf9a132013-01-15 20:16:3354#endif
55
56// TODO(jln): switch to std::numeric_limits<int>::max() when we switch to
57// C++11.
58const size_t kTooBigAllocSize = INT_MAX;
59
60// Detect runtime TCMalloc bypasses.
61bool IsTcMallocBypassed() {
estade@chromium.orgab68db2d2014-04-15 20:58:0962#if defined(OS_LINUX)
jln@chromium.orgb5bf9a132013-01-15 20:16:3363 // This should detect a TCMalloc bypass from Valgrind.
64 char* g_slice = getenv("G_SLICE");
65 if (g_slice && !strcmp(g_slice, "always-malloc"))
66 return true;
wfh@chromium.org949536b2013-06-10 23:48:4967#elif defined(OS_WIN)
68 // This should detect a TCMalloc bypass from setting
69 // the CHROME_ALLOCATOR environment variable.
70 char* allocator = getenv("CHROME_ALLOCATOR");
71 if (allocator && strcmp(allocator, "tcmalloc"))
72 return true;
jln@chromium.orgb5bf9a132013-01-15 20:16:3373#endif
74 return false;
75}
76
jln@chromium.orgd6a6cea2013-02-15 06:10:4077bool CallocDiesOnOOM() {
thakis@chromium.org7d899e9a2013-10-12 06:02:4278// The sanitizers' calloc dies on OOM instead of returning NULL.
jln@chromium.orgd6a6cea2013-02-15 06:10:4079// The wrapper function in base/process_util_linux.cc that is used when we
80// compile without TCMalloc will just die on OOM instead of returning NULL.
timurrrr@chromium.org6c65f2b2014-05-07 08:57:4681#if defined(ADDRESS_SANITIZER) || \
82 defined(MEMORY_SANITIZER) || \
83 defined(THREAD_SANITIZER) || \
84 (defined(OS_LINUX) && defined(NO_TCMALLOC))
jln@chromium.orgd6a6cea2013-02-15 06:10:4085 return true;
86#else
87 return false;
88#endif
89}
90
jln@chromium.orgb5bf9a132013-01-15 20:16:3391// Fake test that allow to know the state of TCMalloc by looking at bots.
jln@chromium.orgd6a6cea2013-02-15 06:10:4092TEST(SecurityTest, TCMALLOC_TEST(IsTCMallocDynamicallyBypassed)) {
jln@chromium.orgb5bf9a132013-01-15 20:16:3393 printf("Malloc is dynamically bypassed: %s\n",
94 IsTcMallocBypassed() ? "yes." : "no.");
95}
96
jln@chromium.orgd6a6cea2013-02-15 06:10:4097// The MemoryAllocationRestrictions* tests test that we can not allocate a
98// memory range that cannot be indexed via an int. This is used to mitigate
99// vulnerabilities in libraries that use int instead of size_t. See
100// crbug.com/169327.
101
102TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsMalloc)) {
jln@chromium.orgb5bf9a132013-01-15 20:16:33103 if (!IsTcMallocBypassed()) {
jln@chromium.orgfe394f32013-02-06 03:23:49104 scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>(
105 HideValueFromCompiler(malloc(kTooBigAllocSize))));
106 ASSERT_TRUE(!ptr);
jln@chromium.orgb5bf9a132013-01-15 20:16:33107 }
108}
109
jln@chromium.orgd6a6cea2013-02-15 06:10:40110TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsCalloc)) {
jln@chromium.orgb5bf9a132013-01-15 20:16:33111 if (!IsTcMallocBypassed()) {
jln@chromium.orgfe394f32013-02-06 03:23:49112 scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>(
113 HideValueFromCompiler(calloc(kTooBigAllocSize, 1))));
114 ASSERT_TRUE(!ptr);
jln@chromium.orgb5bf9a132013-01-15 20:16:33115 }
116}
117
jln@chromium.orgd6a6cea2013-02-15 06:10:40118TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsRealloc)) {
jln@chromium.orgb5bf9a132013-01-15 20:16:33119 if (!IsTcMallocBypassed()) {
120 char* orig_ptr = static_cast<char*>(malloc(1));
jln@chromium.orgfe394f32013-02-06 03:23:49121 ASSERT_TRUE(orig_ptr);
122 scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>(
123 HideValueFromCompiler(realloc(orig_ptr, kTooBigAllocSize))));
124 ASSERT_TRUE(!ptr);
jln@chromium.orgb5bf9a132013-01-15 20:16:33125 // If realloc() did not succeed, we need to free orig_ptr.
126 free(orig_ptr);
127 }
128}
129
130typedef struct {
131 char large_array[kTooBigAllocSize];
132} VeryLargeStruct;
133
jln@chromium.orgd6a6cea2013-02-15 06:10:40134TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsNew)) {
jln@chromium.orgb5bf9a132013-01-15 20:16:33135 if (!IsTcMallocBypassed()) {
jln@chromium.orgfe394f32013-02-06 03:23:49136 scoped_ptr<VeryLargeStruct> ptr(
137 HideValueFromCompiler(new (nothrow) VeryLargeStruct));
138 ASSERT_TRUE(!ptr);
jln@chromium.orgb5bf9a132013-01-15 20:16:33139 }
140}
141
jln@chromium.orgd6a6cea2013-02-15 06:10:40142TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsNewArray)) {
jln@chromium.orgb5bf9a132013-01-15 20:16:33143 if (!IsTcMallocBypassed()) {
jln@chromium.orgfe394f32013-02-06 03:23:49144 scoped_ptr<char[]> ptr(
145 HideValueFromCompiler(new (nothrow) char[kTooBigAllocSize]));
146 ASSERT_TRUE(!ptr);
jln@chromium.org9dd40c832013-01-25 22:47:12147 }
148}
149
jln@chromium.org9c4729b2013-01-26 04:41:15150// The tests bellow check for overflows in new[] and calloc().
151
glider@chromium.orge877a062013-10-24 08:57:19152#if defined(OS_IOS) || defined(OS_WIN) || defined(THREAD_SANITIZER)
153 #define DISABLE_ON_IOS_AND_WIN_AND_TSAN(function) DISABLED_##function
jln@chromium.org9c4729b2013-01-26 04:41:15154#else
glider@chromium.orge877a062013-10-24 08:57:19155 #define DISABLE_ON_IOS_AND_WIN_AND_TSAN(function) function
jln@chromium.org9c4729b2013-01-26 04:41:15156#endif
157
jln@chromium.org9c4729b2013-01-26 04:41:15158// There are platforms where these tests are known to fail. We would like to
159// be able to easily check the status on the bots, but marking tests as
160// FAILS_ is too clunky.
161void OverflowTestsSoftExpectTrue(bool overflow_detected) {
162 if (!overflow_detected) {
163#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_MACOSX)
164 // Sadly, on Linux, Android, and OSX we don't have a good story yet. Don't
165 // fail the test, but report.
166 printf("Platform has overflow: %s\n",
167 !overflow_detected ? "yes." : "no.");
168#else
169 // Otherwise, fail the test. (Note: EXPECT are ok in subfunctions, ASSERT
170 // aren't).
171 EXPECT_TRUE(overflow_detected);
172#endif
173 }
174}
175
jln@chromium.org9c4729b2013-01-26 04:41:15176// Test array[TooBig][X] and array[X][TooBig] allocations for int overflows.
177// IOS doesn't honor nothrow, so disable the test there.
jln@chromium.org1cdfdb72013-04-04 12:02:35178// Crashes on Windows Dbg builds, disable there as well.
glider@chromium.orge877a062013-10-24 08:57:19179TEST(SecurityTest, DISABLE_ON_IOS_AND_WIN_AND_TSAN(NewOverflow)) {
jln@chromium.org9c4729b2013-01-26 04:41:15180 const size_t kArraySize = 4096;
181 // We want something "dynamic" here, so that the compiler doesn't
182 // immediately reject crazy arrays.
183 const size_t kDynamicArraySize = HideValueFromCompiler(kArraySize);
184 // numeric_limits are still not constexpr until we switch to C++11, so we
185 // use an ugly cast.
186 const size_t kMaxSizeT = ~static_cast<size_t>(0);
187 ASSERT_EQ(numeric_limits<size_t>::max(), kMaxSizeT);
188 const size_t kArraySize2 = kMaxSizeT / kArraySize + 10;
189 const size_t kDynamicArraySize2 = HideValueFromCompiler(kArraySize2);
190 {
191 scoped_ptr<char[][kArraySize]> array_pointer(new (nothrow)
192 char[kDynamicArraySize2][kArraySize]);
jln@chromium.orgfe394f32013-02-06 03:23:49193 OverflowTestsSoftExpectTrue(!array_pointer);
jln@chromium.org9c4729b2013-01-26 04:41:15194 }
jln@chromium.org1cdfdb72013-04-04 12:02:35195 // On windows, the compiler prevents static array sizes of more than
196 // 0x7fffffff (error C2148).
197#if !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS)
jln@chromium.org9c4729b2013-01-26 04:41:15198 {
199 scoped_ptr<char[][kArraySize2]> array_pointer(new (nothrow)
200 char[kDynamicArraySize][kArraySize2]);
jln@chromium.orgfe394f32013-02-06 03:23:49201 OverflowTestsSoftExpectTrue(!array_pointer);
jln@chromium.org9c4729b2013-01-26 04:41:15202 }
jln@chromium.org1cdfdb72013-04-04 12:02:35203#endif // !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS)
jln@chromium.org9c4729b2013-01-26 04:41:15204}
205
jln@chromium.orgd6a6cea2013-02-15 06:10:40206// Call calloc(), eventually free the memory and return whether or not
207// calloc() did succeed.
208bool CallocReturnsNull(size_t nmemb, size_t size) {
209 scoped_ptr<char, base::FreeDeleter> array_pointer(
210 static_cast<char*>(calloc(nmemb, size)));
211 // We need the call to HideValueFromCompiler(): we have seen LLVM
212 // optimize away the call to calloc() entirely and assume
213 // the pointer to not be NULL.
214 return HideValueFromCompiler(array_pointer.get()) == NULL;
215}
216
jln@chromium.orgdf191bf2013-04-04 17:24:04217// Test if calloc() can overflow.
hans@chromium.org50a3d9c2013-10-14 19:37:12218TEST(SecurityTest, CallocOverflow) {
jln@chromium.org9c4729b2013-01-26 04:41:15219 const size_t kArraySize = 4096;
220 const size_t kMaxSizeT = numeric_limits<size_t>::max();
221 const size_t kArraySize2 = kMaxSizeT / kArraySize + 10;
jln@chromium.orgd6a6cea2013-02-15 06:10:40222 if (!CallocDiesOnOOM()) {
223 EXPECT_TRUE(CallocReturnsNull(kArraySize, kArraySize2));
224 EXPECT_TRUE(CallocReturnsNull(kArraySize2, kArraySize));
225 } else {
226 // It's also ok for calloc to just terminate the process.
227#if defined(GTEST_HAS_DEATH_TEST)
228 EXPECT_DEATH(CallocReturnsNull(kArraySize, kArraySize2), "");
229 EXPECT_DEATH(CallocReturnsNull(kArraySize2, kArraySize), "");
230#endif // GTEST_HAS_DEATH_TEST
jln@chromium.org9c4729b2013-01-26 04:41:15231 }
232}
233
estade@chromium.orgab68db2d2014-04-15 20:58:09234#if defined(OS_LINUX) && defined(__x86_64__)
jln@chromium.org547683f2013-02-04 23:39:48235// Check if ptr1 and ptr2 are separated by less than size chars.
236bool ArePointersToSameArea(void* ptr1, void* ptr2, size_t size) {
237 ptrdiff_t ptr_diff = reinterpret_cast<char*>(std::max(ptr1, ptr2)) -
238 reinterpret_cast<char*>(std::min(ptr1, ptr2));
239 return static_cast<size_t>(ptr_diff) <= size;
240}
241
jln@chromium.org1b556f82013-01-31 02:23:43242// Check if TCMalloc uses an underlying random memory allocator.
jln@chromium.orgd6a6cea2013-02-15 06:10:40243TEST(SecurityTest, TCMALLOC_TEST(RandomMemoryAllocations)) {
jln@chromium.org1b556f82013-01-31 02:23:43244 if (IsTcMallocBypassed())
245 return;
jln@chromium.org547683f2013-02-04 23:39:48246 size_t kPageSize = 4096; // We support x86_64 only.
247 // Check that malloc() returns an address that is neither the kernel's
248 // un-hinted mmap area, nor the current brk() area. The first malloc() may
249 // not be at a random address because TCMalloc will first exhaust any memory
250 // that it has allocated early on, before starting the sophisticated
251 // allocators.
252 void* default_mmap_heap_address =
253 mmap(0, kPageSize, PROT_READ|PROT_WRITE,
254 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
255 ASSERT_NE(default_mmap_heap_address,
256 static_cast<void*>(MAP_FAILED));
257 ASSERT_EQ(munmap(default_mmap_heap_address, kPageSize), 0);
258 void* brk_heap_address = sbrk(0);
259 ASSERT_NE(brk_heap_address, reinterpret_cast<void*>(-1));
260 ASSERT_TRUE(brk_heap_address != NULL);
261 // 1 MB should get us past what TCMalloc pre-allocated before initializing
262 // the sophisticated allocators.
263 size_t kAllocSize = 1<<20;
264 scoped_ptr<char, base::FreeDeleter> ptr(
265 static_cast<char*>(malloc(kAllocSize)));
266 ASSERT_TRUE(ptr != NULL);
267 // If two pointers are separated by less than 512MB, they are considered
268 // to be in the same area.
269 // Our random pointer could be anywhere within 0x3fffffffffff (46bits),
270 // and we are checking that it's not withing 1GB (30 bits) from two
271 // addresses (brk and mmap heap). We have roughly one chance out of
272 // 2^15 to flake.
273 const size_t kAreaRadius = 1<<29;
274 bool in_default_mmap_heap = ArePointersToSameArea(
275 ptr.get(), default_mmap_heap_address, kAreaRadius);
276 EXPECT_FALSE(in_default_mmap_heap);
277
278 bool in_default_brk_heap = ArePointersToSameArea(
279 ptr.get(), brk_heap_address, kAreaRadius);
280 EXPECT_FALSE(in_default_brk_heap);
281
282 // In the implementation, we always mask our random addresses with
283 // kRandomMask, so we use it as an additional detection mechanism.
284 const uintptr_t kRandomMask = 0x3fffffffffffULL;
285 bool impossible_random_address =
286 reinterpret_cast<uintptr_t>(ptr.get()) & ~kRandomMask;
287 EXPECT_FALSE(impossible_random_address);
jln@chromium.org1b556f82013-01-31 02:23:43288}
289
estade@chromium.orgab68db2d2014-04-15 20:58:09290#endif // defined(OS_LINUX) && defined(__x86_64__)
jln@chromium.org1b556f82013-01-31 02:23:43291
jln@chromium.orgb5bf9a132013-01-15 20:16:33292} // namespace