blob: 0ecaec415412e9a3880203ce8371846bb150aa98 [file] [log] [blame]
jln@chromium.org4faaf212013-01-16 05:16:33 +09001// 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.orgc71a4da2013-01-31 11:23:43 +09005#include <fcntl.h>
jln@chromium.org4faaf212013-01-16 05:16:33 +09006#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
jln@chromium.orgc71a4da2013-01-31 11:23:43 +09009#include <sys/stat.h>
10#include <sys/types.h>
jln@chromium.org4faaf212013-01-16 05:16:33 +090011
12#include <algorithm>
13#include <limits>
14
brettw@chromium.org01f3da42014-08-14 05:22:14 +090015#include "base/files/file_util.h"
jln@chromium.org4faaf212013-01-16 05:16:33 +090016#include "base/logging.h"
17#include "base/memory/scoped_ptr.h"
jln@chromium.orga55baa62013-02-05 08:39:48 +090018#include "build/build_config.h"
jln@chromium.org4faaf212013-01-16 05:16:33 +090019#include "testing/gtest/include/gtest/gtest.h"
20
jln@chromium.orga55baa62013-02-05 08:39:48 +090021#if defined(OS_POSIX)
22#include <sys/mman.h>
23#include <unistd.h>
24#endif
25
jln@chromium.org4faaf212013-01-16 05:16:33 +090026using std::nothrow;
jln@chromium.orgd06052c2013-01-26 13:41:15 +090027using std::numeric_limits;
jln@chromium.org4faaf212013-01-16 05:16:33 +090028
29namespace {
30
jln@chromium.orgc6d44222013-02-06 12:23:49 +090031// 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.org59755862013-04-04 21:02:35 +090037#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.orgc6d44222013-02-06 12:23:49 +090042 return value;
43}
44
dmikurube@chromium.org1df36512014-03-06 05:07:26 +090045// - NO_TCMALLOC (should be defined if compiled with use_allocator!="tcmalloc")
chrisha@google.com87839ab2014-03-28 00:08:04 +090046// - ADDRESS_SANITIZER and SYZYASAN because they have their own memory allocator
jln@chromium.orgfe78d552013-02-15 15:10:40 +090047// - IOS does not use tcmalloc
jln@chromium.org4faaf212013-01-16 05:16:33 +090048// - OS_MACOSX does not use tcmalloc
49#if !defined(NO_TCMALLOC) && !defined(ADDRESS_SANITIZER) && \
chrisha@google.com87839ab2014-03-28 00:08:04 +090050 !defined(OS_IOS) && !defined(OS_MACOSX) && !defined(SYZYASAN)
jln@chromium.orgfe78d552013-02-15 15:10:40 +090051 #define TCMALLOC_TEST(function) function
jln@chromium.org4faaf212013-01-16 05:16:33 +090052#else
jln@chromium.orgfe78d552013-02-15 15:10:40 +090053 #define TCMALLOC_TEST(function) DISABLED_##function
jln@chromium.org4faaf212013-01-16 05:16:33 +090054#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.orgd841d6d2014-04-16 05:58:09 +090062#if defined(OS_LINUX)
jln@chromium.org4faaf212013-01-16 05:16:33 +090063 // 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.org91738f62013-06-11 08:48:49 +090067#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.org4faaf212013-01-16 05:16:33 +090073#endif
74 return false;
75}
76
jln@chromium.orgfe78d552013-02-15 15:10:40 +090077bool CallocDiesOnOOM() {
thakis@chromium.orgf0579262013-10-12 15:02:42 +090078// The sanitizers' calloc dies on OOM instead of returning NULL.
jln@chromium.orgfe78d552013-02-15 15:10:40 +090079// 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.org05680db2014-05-07 17:57:46 +090081#if defined(ADDRESS_SANITIZER) || \
82 defined(MEMORY_SANITIZER) || \
83 defined(THREAD_SANITIZER) || \
84 (defined(OS_LINUX) && defined(NO_TCMALLOC))
jln@chromium.orgfe78d552013-02-15 15:10:40 +090085 return true;
86#else
87 return false;
88#endif
89}
90
jln@chromium.org4faaf212013-01-16 05:16:33 +090091// Fake test that allow to know the state of TCMalloc by looking at bots.
jln@chromium.orgfe78d552013-02-15 15:10:40 +090092TEST(SecurityTest, TCMALLOC_TEST(IsTCMallocDynamicallyBypassed)) {
jln@chromium.org4faaf212013-01-16 05:16:33 +090093 printf("Malloc is dynamically bypassed: %s\n",
94 IsTcMallocBypassed() ? "yes." : "no.");
95}
96
jln@chromium.orgfe78d552013-02-15 15:10:40 +090097// 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.org4faaf212013-01-16 05:16:33 +0900103 if (!IsTcMallocBypassed()) {
jln@chromium.orgc6d44222013-02-06 12:23:49 +0900104 scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>(
105 HideValueFromCompiler(malloc(kTooBigAllocSize))));
106 ASSERT_TRUE(!ptr);
jln@chromium.org4faaf212013-01-16 05:16:33 +0900107 }
108}
109
jln@chromium.orgfe78d552013-02-15 15:10:40 +0900110TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsCalloc)) {
jln@chromium.org4faaf212013-01-16 05:16:33 +0900111 if (!IsTcMallocBypassed()) {
jln@chromium.orgc6d44222013-02-06 12:23:49 +0900112 scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>(
113 HideValueFromCompiler(calloc(kTooBigAllocSize, 1))));
114 ASSERT_TRUE(!ptr);
jln@chromium.org4faaf212013-01-16 05:16:33 +0900115 }
116}
117
jln@chromium.orgfe78d552013-02-15 15:10:40 +0900118TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsRealloc)) {
jln@chromium.org4faaf212013-01-16 05:16:33 +0900119 if (!IsTcMallocBypassed()) {
120 char* orig_ptr = static_cast<char*>(malloc(1));
jln@chromium.orgc6d44222013-02-06 12:23:49 +0900121 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.org4faaf212013-01-16 05:16:33 +0900125 // 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.orgfe78d552013-02-15 15:10:40 +0900134TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsNew)) {
jln@chromium.org4faaf212013-01-16 05:16:33 +0900135 if (!IsTcMallocBypassed()) {
jln@chromium.orgc6d44222013-02-06 12:23:49 +0900136 scoped_ptr<VeryLargeStruct> ptr(
137 HideValueFromCompiler(new (nothrow) VeryLargeStruct));
138 ASSERT_TRUE(!ptr);
jln@chromium.org4faaf212013-01-16 05:16:33 +0900139 }
140}
141
jln@chromium.orgfe78d552013-02-15 15:10:40 +0900142TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsNewArray)) {
jln@chromium.org4faaf212013-01-16 05:16:33 +0900143 if (!IsTcMallocBypassed()) {
jln@chromium.orgc6d44222013-02-06 12:23:49 +0900144 scoped_ptr<char[]> ptr(
145 HideValueFromCompiler(new (nothrow) char[kTooBigAllocSize]));
146 ASSERT_TRUE(!ptr);
jln@chromium.org4faaf212013-01-16 05:16:33 +0900147 }
148}
149
jln@chromium.orgd06052c2013-01-26 13:41:15 +0900150// The tests bellow check for overflows in new[] and calloc().
151
glider@chromium.orge9bbb5a2013-10-24 17:57:19 +0900152#if defined(OS_IOS) || defined(OS_WIN) || defined(THREAD_SANITIZER)
153 #define DISABLE_ON_IOS_AND_WIN_AND_TSAN(function) DISABLED_##function
jln@chromium.orgd06052c2013-01-26 13:41:15 +0900154#else
glider@chromium.orge9bbb5a2013-10-24 17:57:19 +0900155 #define DISABLE_ON_IOS_AND_WIN_AND_TSAN(function) function
jln@chromium.orgd06052c2013-01-26 13:41:15 +0900156#endif
157
jln@chromium.orgd06052c2013-01-26 13:41:15 +0900158// 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.orgd06052c2013-01-26 13:41:15 +0900176// 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.org59755862013-04-04 21:02:35 +0900178// Crashes on Windows Dbg builds, disable there as well.
glider@chromium.orge9bbb5a2013-10-24 17:57:19 +0900179TEST(SecurityTest, DISABLE_ON_IOS_AND_WIN_AND_TSAN(NewOverflow)) {
jln@chromium.orgd06052c2013-01-26 13:41:15 +0900180 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.orgc6d44222013-02-06 12:23:49 +0900193 OverflowTestsSoftExpectTrue(!array_pointer);
jln@chromium.orgd06052c2013-01-26 13:41:15 +0900194 }
jln@chromium.org59755862013-04-04 21:02:35 +0900195 // 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.orgd06052c2013-01-26 13:41:15 +0900198 {
199 scoped_ptr<char[][kArraySize2]> array_pointer(new (nothrow)
200 char[kDynamicArraySize][kArraySize2]);
jln@chromium.orgc6d44222013-02-06 12:23:49 +0900201 OverflowTestsSoftExpectTrue(!array_pointer);
jln@chromium.orgd06052c2013-01-26 13:41:15 +0900202 }
jln@chromium.org59755862013-04-04 21:02:35 +0900203#endif // !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS)
jln@chromium.orgd06052c2013-01-26 13:41:15 +0900204}
205
jln@chromium.orgfe78d552013-02-15 15:10:40 +0900206// 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.orgd0103442013-04-05 02:24:04 +0900217// Test if calloc() can overflow.
hans@chromium.orgdbe24752013-10-15 04:37:12 +0900218TEST(SecurityTest, CallocOverflow) {
jln@chromium.orgd06052c2013-01-26 13:41:15 +0900219 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.orgfe78d552013-02-15 15:10:40 +0900222 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.orgd06052c2013-01-26 13:41:15 +0900231 }
232}
233
estade@chromium.orgd841d6d2014-04-16 05:58:09 +0900234#if defined(OS_LINUX) && defined(__x86_64__)
jln@chromium.orga55baa62013-02-05 08:39:48 +0900235// 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.orgc71a4da2013-01-31 11:23:43 +0900242// Check if TCMalloc uses an underlying random memory allocator.
jln@chromium.orgfe78d552013-02-15 15:10:40 +0900243TEST(SecurityTest, TCMALLOC_TEST(RandomMemoryAllocations)) {
jln@chromium.orgc71a4da2013-01-31 11:23:43 +0900244 if (IsTcMallocBypassed())
245 return;
jln@chromium.orga55baa62013-02-05 08:39:48 +0900246 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.orgc71a4da2013-01-31 11:23:43 +0900288}
289
estade@chromium.orgd841d6d2014-04-16 05:58:09 +0900290#endif // defined(OS_LINUX) && defined(__x86_64__)
jln@chromium.orgc71a4da2013-01-31 11:23:43 +0900291
jln@chromium.org4faaf212013-01-16 05:16:33 +0900292} // namespace