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