• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 // This file contains intentional memory errors, some of which may lead to
11 // crashes if the test is ran without special memory testing tools. We use these
12 // errors to verify the sanity of the tools.
13 
14 #include <stddef.h>
15 
16 #include "base/atomicops.h"
17 #include "base/cfi_buildflags.h"
18 #include "base/debug/asan_invalid_access.h"
19 #include "base/debug/profiler.h"
20 #include "base/logging.h"
21 #include "base/memory/raw_ptr.h"
22 #include "base/notreached.h"
23 #include "base/sanitizer_buildflags.h"
24 #include "base/threading/thread.h"
25 #include "build/build_config.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 #include "third_party/abseil-cpp/absl/base/dynamic_annotations.h"
28 
29 #if BUILDFLAG(IS_WIN)
30 #include <windows.h>
31 #else
32 #include <dlfcn.h>
33 #endif
34 
35 namespace base {
36 
37 namespace {
38 
39 const base::subtle::Atomic32 kMagicValue = 42;
40 
41 // Helper for memory accesses that can potentially corrupt memory or cause a
42 // crash during a native run.
43 #if defined(ADDRESS_SANITIZER)
44 #define HARMFUL_ACCESS(action, error_regexp) \
45   EXPECT_DEATH_IF_SUPPORTED(action, error_regexp)
46 #elif BUILDFLAG(IS_HWASAN)
47 #define HARMFUL_ACCESS(action, error_regexp) \
48   EXPECT_DEATH(action, "tag-mismatch")
49 #else
50 #define HARMFUL_ACCESS(action, error_regexp)
51 #define HARMFUL_ACCESS_IS_NOOP
52 #endif
53 
DoReadUninitializedValue(volatile char * ptr)54 void DoReadUninitializedValue(volatile char *ptr) {
55   // Comparison with 64 is to prevent clang from optimizing away the
56   // jump -- valgrind only catches jumps and conditional moves, but clang uses
57   // the borrow flag if the condition is just `*ptr == '\0'`.  We no longer
58   // support valgrind, but this constant should be fine to keep as-is.
59   if (*ptr == 64) {
60     VLOG(1) << "Uninit condition is true";
61   } else {
62     VLOG(1) << "Uninit condition is false";
63   }
64 }
65 
ReadUninitializedValue(volatile char * ptr)66 void ReadUninitializedValue(volatile char *ptr) {
67 #if defined(MEMORY_SANITIZER)
68   EXPECT_DEATH(DoReadUninitializedValue(ptr),
69                "use-of-uninitialized-value");
70 #else
71   DoReadUninitializedValue(ptr);
72 #endif
73 }
74 
75 #ifndef HARMFUL_ACCESS_IS_NOOP
ReadValueOutOfArrayBoundsLeft(char * ptr)76 void ReadValueOutOfArrayBoundsLeft(char *ptr) {
77   char c = ptr[-2];
78   VLOG(1) << "Reading a byte out of bounds: " << c;
79 }
80 
ReadValueOutOfArrayBoundsRight(char * ptr,size_t size)81 void ReadValueOutOfArrayBoundsRight(char *ptr, size_t size) {
82   char c = ptr[size + 1];
83   VLOG(1) << "Reading a byte out of bounds: " << c;
84 }
85 
WriteValueOutOfArrayBoundsLeft(char * ptr)86 void WriteValueOutOfArrayBoundsLeft(char *ptr) {
87   ptr[-1] = kMagicValue;
88 }
89 
WriteValueOutOfArrayBoundsRight(char * ptr,size_t size)90 void WriteValueOutOfArrayBoundsRight(char *ptr, size_t size) {
91   ptr[size] = kMagicValue;
92 }
93 #endif  // HARMFUL_ACCESS_IS_NOOP
94 
MakeSomeErrors(char * ptr,size_t size)95 void MakeSomeErrors(char *ptr, size_t size) {
96   ReadUninitializedValue(ptr);
97 
98   HARMFUL_ACCESS(ReadValueOutOfArrayBoundsLeft(ptr), "2 bytes before");
99   HARMFUL_ACCESS(ReadValueOutOfArrayBoundsRight(ptr, size), "1 bytes after");
100   HARMFUL_ACCESS(WriteValueOutOfArrayBoundsLeft(ptr), "1 bytes before");
101   HARMFUL_ACCESS(WriteValueOutOfArrayBoundsRight(ptr, size), "0 bytes after");
102 }
103 
104 }  // namespace
105 
106 #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) ||  \
107     defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \
108     defined(UNDEFINED_SANITIZER)
109 // build/sanitizers/sanitizer_options.cc defines symbols like
110 // __asan_default_options which the sanitizer runtime calls if they exist
111 // in the executable. If they don't, the sanitizer runtime silently uses an
112 // internal default value instead. The build puts the symbol
113 // _sanitizer_options_link_helper (which the sanitizer runtime doesn't know
114 // about, it's a chrome thing) in that file and then tells the linker that
115 // that symbol must exist. This causes sanitizer_options.cc to be part of
116 // our binaries, which in turn makes sure our __asan_default_options are used.
117 // We had problems with __asan_default_options not being used, so this test
118 // verifies that _sanitizer_options_link_helper actually makes it into our
119 // binaries.
120 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
121 // TODO(crbug.com/40224191): Sanitizer options are currently broken
122 // on Android.
123 // TODO(crbug.com/40223949): __asan_default_options should be used
124 // on Windows too, but currently isn't.
125 #define MAYBE_LinksSanitizerOptions DISABLED_LinksSanitizerOptions
126 #else
127 #define MAYBE_LinksSanitizerOptions LinksSanitizerOptions
128 #endif
TEST(ToolsSanityTest,MAYBE_LinksSanitizerOptions)129 TEST(ToolsSanityTest, MAYBE_LinksSanitizerOptions) {
130   constexpr char kSym[] = "_sanitizer_options_link_helper";
131 #if BUILDFLAG(IS_WIN)
132   auto sym = GetProcAddress(GetModuleHandle(nullptr), kSym);
133 #else
134   void* sym = dlsym(RTLD_DEFAULT, kSym);
135 #endif
136   EXPECT_TRUE(sym != nullptr);
137 }
138 #endif  // sanitizers
139 
140 // A memory leak detector should report an error in this test.
TEST(ToolsSanityTest,MemoryLeak)141 TEST(ToolsSanityTest, MemoryLeak) {
142   // Without the |volatile|, clang optimizes away the next two lines.
143   int* volatile leak = new int[256];  // Leak some memory intentionally.
144   leak[4] = 1;  // Make sure the allocated memory is used.
145 }
146 
TEST(ToolsSanityTest,AccessesToNewMemory)147 TEST(ToolsSanityTest, AccessesToNewMemory) {
148   char* foo = new char[16];
149   MakeSomeErrors(foo, 16);
150   delete [] foo;
151   // Use after delete.
152   HARMFUL_ACCESS(foo[5] = 0, "heap-use-after-free");
153 }
154 
TEST(ToolsSanityTest,AccessesToMallocMemory)155 TEST(ToolsSanityTest, AccessesToMallocMemory) {
156   char* foo = reinterpret_cast<char*>(malloc(16));
157   MakeSomeErrors(foo, 16);
158   free(foo);
159   // Use after free.
160   HARMFUL_ACCESS(foo[5] = 0, "heap-use-after-free");
161 }
162 
TEST(ToolsSanityTest,AccessesToStack)163 TEST(ToolsSanityTest, AccessesToStack) {
164   char foo[16];
165 
166   ReadUninitializedValue(foo);
167   HARMFUL_ACCESS(ReadValueOutOfArrayBoundsLeft(foo),
168                  "underflows this variable");
169   HARMFUL_ACCESS(ReadValueOutOfArrayBoundsRight(foo, 16),
170                  "overflows this variable");
171   HARMFUL_ACCESS(WriteValueOutOfArrayBoundsLeft(foo),
172                  "underflows this variable");
173   HARMFUL_ACCESS(WriteValueOutOfArrayBoundsRight(foo, 16),
174                  "overflows this variable");
175 }
176 
177 #if defined(ADDRESS_SANITIZER)
178 
179 // alloc_dealloc_mismatch defaults to
180 // !SANITIZER_MAC && !SANITIZER_WINDOWS && !SANITIZER_ANDROID,
181 // in the sanitizer runtime upstream.
182 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \
183     BUILDFLAG(IS_FUCHSIA)
184 #define MAYBE_SingleElementDeletedWithBraces \
185     DISABLED_SingleElementDeletedWithBraces
186 #define MAYBE_ArrayDeletedWithoutBraces DISABLED_ArrayDeletedWithoutBraces
187 #else
188 #define MAYBE_ArrayDeletedWithoutBraces ArrayDeletedWithoutBraces
189 #define MAYBE_SingleElementDeletedWithBraces SingleElementDeletedWithBraces
190 #endif  // defined(ADDRESS_SANITIZER)
191 
allocateArray()192 static int* allocateArray() {
193   // Clang warns about the mismatched new[]/delete if they occur in the same
194   // function.
195   return new int[10];
196 }
197 
198 // This test may corrupt memory if not compiled with AddressSanitizer.
TEST(ToolsSanityTest,MAYBE_ArrayDeletedWithoutBraces)199 TEST(ToolsSanityTest, MAYBE_ArrayDeletedWithoutBraces) {
200   // Without the |volatile|, clang optimizes away the next two lines.
201   int* volatile foo = allocateArray();
202   HARMFUL_ACCESS(delete foo, "alloc-dealloc-mismatch");
203   // Under ASan the crash happens in the process spawned by HARMFUL_ACCESS,
204   // need to free the memory in the parent.
205   delete [] foo;
206 }
207 
allocateScalar()208 static int* allocateScalar() {
209   // Clang warns about the mismatched new/delete[] if they occur in the same
210   // function.
211   return new int;
212 }
213 
214 // This test may corrupt memory if not compiled with AddressSanitizer.
TEST(ToolsSanityTest,MAYBE_SingleElementDeletedWithBraces)215 TEST(ToolsSanityTest, MAYBE_SingleElementDeletedWithBraces) {
216   // Without the |volatile|, clang optimizes away the next two lines.
217   int* volatile foo = allocateScalar();
218   (void) foo;
219   HARMFUL_ACCESS(delete [] foo, "alloc-dealloc-mismatch");
220   // Under ASan the crash happens in the process spawned by HARMFUL_ACCESS,
221   // need to free the memory in the parent.
222   delete foo;
223 }
224 #endif
225 
TEST(ToolsSanityTest,DISABLED_AddressSanitizerNullDerefCrashTest)226 TEST(ToolsSanityTest, DISABLED_AddressSanitizerNullDerefCrashTest) {
227   // Intentionally crash to make sure AddressSanitizer is running.
228   // This test should not be ran on bots.
229   int* volatile zero = NULL;
230   *zero = 0;
231 }
232 
TEST(ToolsSanityTest,DISABLED_AddressSanitizerLocalOOBCrashTest)233 TEST(ToolsSanityTest, DISABLED_AddressSanitizerLocalOOBCrashTest) {
234   // Intentionally crash to make sure AddressSanitizer is instrumenting
235   // the local variables.
236   // This test should not be ran on bots.
237   int array[5];
238   // Work around the OOB warning reported by Clang.
239   int* volatile access = &array[5];
240   *access = 43;
241 }
242 
243 namespace {
244 int g_asan_test_global_array[10];
245 }  // namespace
246 
TEST(ToolsSanityTest,DISABLED_AddressSanitizerGlobalOOBCrashTest)247 TEST(ToolsSanityTest, DISABLED_AddressSanitizerGlobalOOBCrashTest) {
248   // Intentionally crash to make sure AddressSanitizer is instrumenting
249   // the global variables.
250   // This test should not be ran on bots.
251 
252   // Work around the OOB warning reported by Clang.
253   int* volatile access = g_asan_test_global_array - 1;
254   *access = 43;
255 }
256 
257 #ifndef HARMFUL_ACCESS_IS_NOOP
TEST(ToolsSanityTest,AsanHeapOverflow)258 TEST(ToolsSanityTest, AsanHeapOverflow) {
259   HARMFUL_ACCESS(debug::AsanHeapOverflow(), "after");
260 }
261 
TEST(ToolsSanityTest,AsanHeapUnderflow)262 TEST(ToolsSanityTest, AsanHeapUnderflow) {
263   HARMFUL_ACCESS(debug::AsanHeapUnderflow(), "before");
264 }
265 
TEST(ToolsSanityTest,AsanHeapUseAfterFree)266 TEST(ToolsSanityTest, AsanHeapUseAfterFree) {
267   HARMFUL_ACCESS(debug::AsanHeapUseAfterFree(), "heap-use-after-free");
268 }
269 
270 #if BUILDFLAG(IS_WIN)
271 // The ASAN runtime doesn't detect heap corruption, this needs fixing before
272 // ASAN builds can ship to the wild. See https://crbug.com/818747.
TEST(ToolsSanityTest,DISABLED_AsanCorruptHeapBlock)273 TEST(ToolsSanityTest, DISABLED_AsanCorruptHeapBlock) {
274   HARMFUL_ACCESS(debug::AsanCorruptHeapBlock(), "");
275 }
276 
TEST(ToolsSanityTest,DISABLED_AsanCorruptHeap)277 TEST(ToolsSanityTest, DISABLED_AsanCorruptHeap) {
278   // This test will kill the process by raising an exception, there's no
279   // particular string to look for in the stack trace.
280   EXPECT_DEATH(debug::AsanCorruptHeap(), "");
281 }
282 #endif  // BUILDFLAG(IS_WIN)
283 #endif  // !HARMFUL_ACCESS_IS_NOOP
284 
285 namespace {
286 
287 // We use caps here just to ensure that the method name doesn't interfere with
288 // the wildcarded suppressions.
289 class TOOLS_SANITY_TEST_CONCURRENT_THREAD : public PlatformThread::Delegate {
290  public:
TOOLS_SANITY_TEST_CONCURRENT_THREAD(bool * value)291   explicit TOOLS_SANITY_TEST_CONCURRENT_THREAD(bool *value) : value_(value) {}
292   ~TOOLS_SANITY_TEST_CONCURRENT_THREAD() override = default;
ThreadMain()293   void ThreadMain() override {
294     *value_ = true;
295 
296     // Sleep for a few milliseconds so the two threads are more likely to live
297     // simultaneously. Otherwise we may miss the report due to mutex
298     // lock/unlock's inside thread creation code in pure-happens-before mode...
299     PlatformThread::Sleep(Milliseconds(100));
300   }
301  private:
302   raw_ptr<bool> value_;
303 };
304 
305 class ReleaseStoreThread : public PlatformThread::Delegate {
306  public:
ReleaseStoreThread(base::subtle::Atomic32 * value)307   explicit ReleaseStoreThread(base::subtle::Atomic32 *value) : value_(value) {}
308   ~ReleaseStoreThread() override = default;
ThreadMain()309   void ThreadMain() override {
310     base::subtle::Release_Store(value_, kMagicValue);
311 
312     // Sleep for a few milliseconds so the two threads are more likely to live
313     // simultaneously. Otherwise we may miss the report due to mutex
314     // lock/unlock's inside thread creation code in pure-happens-before mode...
315     PlatformThread::Sleep(Milliseconds(100));
316   }
317  private:
318   raw_ptr<base::subtle::Atomic32> value_;
319 };
320 
321 class AcquireLoadThread : public PlatformThread::Delegate {
322  public:
AcquireLoadThread(base::subtle::Atomic32 * value)323   explicit AcquireLoadThread(base::subtle::Atomic32 *value) : value_(value) {}
324   ~AcquireLoadThread() override = default;
ThreadMain()325   void ThreadMain() override {
326     // Wait for the other thread to make Release_Store
327     PlatformThread::Sleep(Milliseconds(100));
328     base::subtle::Acquire_Load(value_);
329   }
330  private:
331   raw_ptr<base::subtle::Atomic32> value_;
332 };
333 
RunInParallel(PlatformThread::Delegate * d1,PlatformThread::Delegate * d2)334 void RunInParallel(PlatformThread::Delegate *d1, PlatformThread::Delegate *d2) {
335   PlatformThreadHandle a;
336   PlatformThreadHandle b;
337   PlatformThread::Create(0, d1, &a);
338   PlatformThread::Create(0, d2, &b);
339   PlatformThread::Join(a);
340   PlatformThread::Join(b);
341 }
342 
343 #if defined(THREAD_SANITIZER)
DataRace()344 void DataRace() {
345   bool *shared = new bool(false);
346   TOOLS_SANITY_TEST_CONCURRENT_THREAD thread1(shared), thread2(shared);
347   RunInParallel(&thread1, &thread2);
348   EXPECT_TRUE(*shared);
349   delete shared;
350   // We're in a death test - crash.
351   NOTREACHED();
352 }
353 #endif
354 
355 }  // namespace
356 
357 #if defined(THREAD_SANITIZER)
358 // A data race detector should report an error in this test.
TEST(ToolsSanityTest,DataRace)359 TEST(ToolsSanityTest, DataRace) {
360   // The suppression regexp must match that in base/debug/tsan_suppressions.cc.
361   EXPECT_DEATH(DataRace(), "1 race:base/tools_sanity_unittest.cc");
362 }
363 #endif
364 
TEST(ToolsSanityTest,AnnotateBenignRace)365 TEST(ToolsSanityTest, AnnotateBenignRace) {
366   bool shared = false;
367   ABSL_ANNOTATE_BENIGN_RACE(
368       &shared, "Intentional race - make sure doesn't show up");
369   TOOLS_SANITY_TEST_CONCURRENT_THREAD thread1(&shared), thread2(&shared);
370   RunInParallel(&thread1, &thread2);
371   EXPECT_TRUE(shared);
372 }
373 
TEST(ToolsSanityTest,AtomicsAreIgnored)374 TEST(ToolsSanityTest, AtomicsAreIgnored) {
375   base::subtle::Atomic32 shared = 0;
376   ReleaseStoreThread thread1(&shared);
377   AcquireLoadThread thread2(&shared);
378   RunInParallel(&thread1, &thread2);
379   EXPECT_EQ(kMagicValue, shared);
380 }
381 
382 #if BUILDFLAG(CFI_ENFORCEMENT_TRAP)
383 #if BUILDFLAG(IS_WIN)
384 #define CFI_ERROR_MSG "EXCEPTION_ILLEGAL_INSTRUCTION"
385 #elif BUILDFLAG(IS_ANDROID)
386 // TODO(pcc): Produce proper stack dumps on Android and test for the correct
387 // si_code here.
388 #define CFI_ERROR_MSG "^$"
389 #else
390 #define CFI_ERROR_MSG "ILL_ILLOPN"
391 #endif
392 #elif BUILDFLAG(CFI_ENFORCEMENT_DIAGNOSTIC)
393 #define CFI_ERROR_MSG "runtime error: control flow integrity check"
394 #endif  // BUILDFLAG(CFI_ENFORCEMENT_TRAP || CFI_ENFORCEMENT_DIAGNOSTIC)
395 
396 #if defined(CFI_ERROR_MSG)
397 class A {
398  public:
A()399   A(): n_(0) {}
f()400   virtual void f() { n_++; }
401  protected:
402   int n_;
403 };
404 
405 class B: public A {
406  public:
f()407   void f() override { n_--; }
408 };
409 
410 class C: public B {
411  public:
f()412   void f() override { n_ += 2; }
413 };
414 
KillVptrAndCall(A * obj)415 NOINLINE void KillVptrAndCall(A *obj) {
416   *reinterpret_cast<void **>(obj) = 0;
417   obj->f();
418 }
419 
TEST(ToolsSanityTest,BadVirtualCallNull)420 TEST(ToolsSanityTest, BadVirtualCallNull) {
421   A a;
422   B b;
423   EXPECT_DEATH({ KillVptrAndCall(&a); KillVptrAndCall(&b); }, CFI_ERROR_MSG);
424 }
425 
OverwriteVptrAndCall(B * obj,A * vptr)426 NOINLINE void OverwriteVptrAndCall(B *obj, A *vptr) {
427   *reinterpret_cast<void **>(obj) = *reinterpret_cast<void **>(vptr);
428   obj->f();
429 }
430 
TEST(ToolsSanityTest,BadVirtualCallWrongType)431 TEST(ToolsSanityTest, BadVirtualCallWrongType) {
432   A a;
433   B b;
434   C c;
435   EXPECT_DEATH({ OverwriteVptrAndCall(&b, &a); OverwriteVptrAndCall(&b, &c); },
436                CFI_ERROR_MSG);
437 }
438 
439 // TODO(pcc): remove CFI_CAST_CHECK, see https://crbug.com/626794.
440 #if BUILDFLAG(CFI_CAST_CHECK)
TEST(ToolsSanityTest,BadDerivedCast)441 TEST(ToolsSanityTest, BadDerivedCast) {
442   A a;
443   EXPECT_DEATH((void)(B*)&a, CFI_ERROR_MSG);
444 }
445 
TEST(ToolsSanityTest,BadUnrelatedCast)446 TEST(ToolsSanityTest, BadUnrelatedCast) {
447   class A {
448     virtual void f() {}
449   };
450 
451   class B {
452     virtual void f() {}
453   };
454 
455   A a;
456   EXPECT_DEATH((void)(B*)&a, CFI_ERROR_MSG);
457 }
458 #endif  // BUILDFLAG(CFI_CAST_CHECK)
459 
460 #endif  // CFI_ERROR_MSG
461 
462 #undef CFI_ERROR_MSG
463 #undef HARMFUL_ACCESS
464 #undef HARMFUL_ACCESS_IS_NOOP
465 
466 }  // namespace base
467