summaryrefslogtreecommitdiff
path: root/base/tools_sanity_unittest.cc
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-07-15 01:26:09 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-07-15 01:26:09 +0000
commit536b96d958dab9d943af978463fe82fa084d0042 (patch)
tree0f4d08540c8e5fa16bcd0c99cd413fe815560e4b /base/tools_sanity_unittest.cc
parentb4233fd961752b80d42ae68e8b42acfb3e99be57 (diff)
parent0d51dc717edd4d97116b47bc156e83b8fa193d3d (diff)
downloadlibchrome-536b96d958dab9d943af978463fe82fa084d0042.tar.gz
Snap for 7550844 from 0d51dc717edd4d97116b47bc156e83b8fa193d3d to mainline-conscrypt-releaseandroid-mainline-12.0.0_r8android-mainline-12.0.0_r25android12-mainline-conscrypt-release
Change-Id: I4679185e58ca2896cc6470a6441557faff4b018e
Diffstat (limited to 'base/tools_sanity_unittest.cc')
-rw-r--r--base/tools_sanity_unittest.cc423
1 files changed, 423 insertions, 0 deletions
diff --git a/base/tools_sanity_unittest.cc b/base/tools_sanity_unittest.cc
new file mode 100644
index 0000000000..98c30df47d
--- /dev/null
+++ b/base/tools_sanity_unittest.cc
@@ -0,0 +1,423 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains intentional memory errors, some of which may lead to
+// crashes if the test is ran without special memory testing tools. We use these
+// errors to verify the sanity of the tools.
+
+#include <stddef.h>
+
+#include "base/atomicops.h"
+#include "base/cfi_buildflags.h"
+#include "base/debug/asan_invalid_access.h"
+#include "base/debug/profiler.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+const base::subtle::Atomic32 kMagicValue = 42;
+
+// Helper for memory accesses that can potentially corrupt memory or cause a
+// crash during a native run.
+#if defined(ADDRESS_SANITIZER)
+#if defined(OS_IOS)
+// EXPECT_DEATH is not supported on IOS.
+#define HARMFUL_ACCESS(action,error_regexp) do { action; } while (0)
+#else
+#define HARMFUL_ACCESS(action,error_regexp) EXPECT_DEATH(action,error_regexp)
+#endif // !OS_IOS
+#else
+#define HARMFUL_ACCESS(action, error_regexp)
+#define HARMFUL_ACCESS_IS_NOOP
+#endif
+
+void DoReadUninitializedValue(char *ptr) {
+ // Comparison with 64 is to prevent clang from optimizing away the
+ // jump -- valgrind only catches jumps and conditional moves, but clang uses
+ // the borrow flag if the condition is just `*ptr == '\0'`. We no longer
+ // support valgrind, but this constant should be fine to keep as-is.
+ if (*ptr == 64) {
+ VLOG(1) << "Uninit condition is true";
+ } else {
+ VLOG(1) << "Uninit condition is false";
+ }
+}
+
+void ReadUninitializedValue(char *ptr) {
+#if defined(MEMORY_SANITIZER)
+ EXPECT_DEATH(DoReadUninitializedValue(ptr),
+ "use-of-uninitialized-value");
+#else
+ DoReadUninitializedValue(ptr);
+#endif
+}
+
+#ifndef HARMFUL_ACCESS_IS_NOOP
+void ReadValueOutOfArrayBoundsLeft(char *ptr) {
+ char c = ptr[-2];
+ VLOG(1) << "Reading a byte out of bounds: " << c;
+}
+
+void ReadValueOutOfArrayBoundsRight(char *ptr, size_t size) {
+ char c = ptr[size + 1];
+ VLOG(1) << "Reading a byte out of bounds: " << c;
+}
+
+void WriteValueOutOfArrayBoundsLeft(char *ptr) {
+ ptr[-1] = kMagicValue;
+}
+
+void WriteValueOutOfArrayBoundsRight(char *ptr, size_t size) {
+ ptr[size] = kMagicValue;
+}
+#endif // HARMFUL_ACCESS_IS_NOOP
+
+void MakeSomeErrors(char *ptr, size_t size) {
+ ReadUninitializedValue(ptr);
+
+ HARMFUL_ACCESS(ReadValueOutOfArrayBoundsLeft(ptr),
+ "2 bytes to the left");
+ HARMFUL_ACCESS(ReadValueOutOfArrayBoundsRight(ptr, size),
+ "1 bytes to the right");
+ HARMFUL_ACCESS(WriteValueOutOfArrayBoundsLeft(ptr),
+ "1 bytes to the left");
+ HARMFUL_ACCESS(WriteValueOutOfArrayBoundsRight(ptr, size),
+ "0 bytes to the right");
+}
+
+} // namespace
+
+// A memory leak detector should report an error in this test.
+TEST(ToolsSanityTest, MemoryLeak) {
+ // Without the |volatile|, clang optimizes away the next two lines.
+ int* volatile leak = new int[256]; // Leak some memory intentionally.
+ leak[4] = 1; // Make sure the allocated memory is used.
+}
+
+#if (defined(ADDRESS_SANITIZER) && defined(OS_IOS))
+// Because iOS doesn't support death tests, each of the following tests will
+// crash the whole program under Asan.
+#define MAYBE_AccessesToNewMemory DISABLED_AccessesToNewMemory
+#define MAYBE_AccessesToMallocMemory DISABLED_AccessesToMallocMemory
+#else
+#define MAYBE_AccessesToNewMemory AccessesToNewMemory
+#define MAYBE_AccessesToMallocMemory AccessesToMallocMemory
+#endif // (defined(ADDRESS_SANITIZER) && defined(OS_IOS))
+
+// The following tests pass with Clang r170392, but not r172454, which
+// makes AddressSanitizer detect errors in them. We disable these tests under
+// AddressSanitizer until we fully switch to Clang r172454. After that the
+// tests should be put back under the (defined(OS_IOS) || defined(OS_WIN))
+// clause above.
+// See also http://crbug.com/172614.
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_SingleElementDeletedWithBraces \
+ DISABLED_SingleElementDeletedWithBraces
+#define MAYBE_ArrayDeletedWithoutBraces DISABLED_ArrayDeletedWithoutBraces
+#else
+#define MAYBE_ArrayDeletedWithoutBraces ArrayDeletedWithoutBraces
+#define MAYBE_SingleElementDeletedWithBraces SingleElementDeletedWithBraces
+#endif // defined(ADDRESS_SANITIZER)
+
+TEST(ToolsSanityTest, MAYBE_AccessesToNewMemory) {
+ char *foo = new char[10];
+ MakeSomeErrors(foo, 10);
+ delete [] foo;
+ // Use after delete.
+ HARMFUL_ACCESS(foo[5] = 0, "heap-use-after-free");
+}
+
+TEST(ToolsSanityTest, MAYBE_AccessesToMallocMemory) {
+ char *foo = reinterpret_cast<char*>(malloc(10));
+ MakeSomeErrors(foo, 10);
+ free(foo);
+ // Use after free.
+ HARMFUL_ACCESS(foo[5] = 0, "heap-use-after-free");
+}
+
+#if defined(ADDRESS_SANITIZER)
+
+static int* allocateArray() {
+ // Clang warns about the mismatched new[]/delete if they occur in the same
+ // function.
+ return new int[10];
+}
+
+// This test may corrupt memory if not compiled with AddressSanitizer.
+TEST(ToolsSanityTest, MAYBE_ArrayDeletedWithoutBraces) {
+ // Without the |volatile|, clang optimizes away the next two lines.
+ int* volatile foo = allocateArray();
+ delete foo;
+}
+#endif
+
+#if defined(ADDRESS_SANITIZER)
+static int* allocateScalar() {
+ // Clang warns about the mismatched new/delete[] if they occur in the same
+ // function.
+ return new int;
+}
+
+// This test may corrupt memory if not compiled with AddressSanitizer.
+TEST(ToolsSanityTest, MAYBE_SingleElementDeletedWithBraces) {
+ // Without the |volatile|, clang optimizes away the next two lines.
+ int* volatile foo = allocateScalar();
+ (void) foo;
+ delete [] foo;
+}
+#endif
+
+#if defined(ADDRESS_SANITIZER)
+
+TEST(ToolsSanityTest, DISABLED_AddressSanitizerNullDerefCrashTest) {
+ // Intentionally crash to make sure AddressSanitizer is running.
+ // This test should not be ran on bots.
+ int* volatile zero = NULL;
+ *zero = 0;
+}
+
+TEST(ToolsSanityTest, DISABLED_AddressSanitizerLocalOOBCrashTest) {
+ // Intentionally crash to make sure AddressSanitizer is instrumenting
+ // the local variables.
+ // This test should not be ran on bots.
+ int array[5];
+ // Work around the OOB warning reported by Clang.
+ int* volatile access = &array[5];
+ *access = 43;
+}
+
+namespace {
+int g_asan_test_global_array[10];
+} // namespace
+
+TEST(ToolsSanityTest, DISABLED_AddressSanitizerGlobalOOBCrashTest) {
+ // Intentionally crash to make sure AddressSanitizer is instrumenting
+ // the global variables.
+ // This test should not be ran on bots.
+
+ // Work around the OOB warning reported by Clang.
+ int* volatile access = g_asan_test_global_array - 1;
+ *access = 43;
+}
+
+#ifndef HARMFUL_ACCESS_IS_NOOP
+TEST(ToolsSanityTest, AsanHeapOverflow) {
+ HARMFUL_ACCESS(debug::AsanHeapOverflow() ,"to the right");
+}
+
+TEST(ToolsSanityTest, AsanHeapUnderflow) {
+ HARMFUL_ACCESS(debug::AsanHeapUnderflow(), "to the left");
+}
+
+TEST(ToolsSanityTest, AsanHeapUseAfterFree) {
+ HARMFUL_ACCESS(debug::AsanHeapUseAfterFree(), "heap-use-after-free");
+}
+
+#if defined(OS_WIN)
+// The ASAN runtime doesn't detect heap corruption, this needs fixing before
+// ASAN builds can ship to the wild. See https://crbug.com/818747.
+TEST(ToolsSanityTest, DISABLED_AsanCorruptHeapBlock) {
+ HARMFUL_ACCESS(debug::AsanCorruptHeapBlock(), "");
+}
+
+TEST(ToolsSanityTest, DISABLED_AsanCorruptHeap) {
+ // This test will kill the process by raising an exception, there's no
+ // particular string to look for in the stack trace.
+ EXPECT_DEATH(debug::AsanCorruptHeap(), "");
+}
+#endif // OS_WIN
+#endif // !HARMFUL_ACCESS_IS_NOOP
+
+#endif // ADDRESS_SANITIZER
+
+namespace {
+
+// We use caps here just to ensure that the method name doesn't interfere with
+// the wildcarded suppressions.
+class TOOLS_SANITY_TEST_CONCURRENT_THREAD : public PlatformThread::Delegate {
+ public:
+ explicit TOOLS_SANITY_TEST_CONCURRENT_THREAD(bool *value) : value_(value) {}
+ ~TOOLS_SANITY_TEST_CONCURRENT_THREAD() override = default;
+ void ThreadMain() override {
+ *value_ = true;
+
+ // Sleep for a few milliseconds so the two threads are more likely to live
+ // simultaneously. Otherwise we may miss the report due to mutex
+ // lock/unlock's inside thread creation code in pure-happens-before mode...
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ }
+ private:
+ bool *value_;
+};
+
+class ReleaseStoreThread : public PlatformThread::Delegate {
+ public:
+ explicit ReleaseStoreThread(base::subtle::Atomic32 *value) : value_(value) {}
+ ~ReleaseStoreThread() override = default;
+ void ThreadMain() override {
+ base::subtle::Release_Store(value_, kMagicValue);
+
+ // Sleep for a few milliseconds so the two threads are more likely to live
+ // simultaneously. Otherwise we may miss the report due to mutex
+ // lock/unlock's inside thread creation code in pure-happens-before mode...
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ }
+ private:
+ base::subtle::Atomic32 *value_;
+};
+
+class AcquireLoadThread : public PlatformThread::Delegate {
+ public:
+ explicit AcquireLoadThread(base::subtle::Atomic32 *value) : value_(value) {}
+ ~AcquireLoadThread() override = default;
+ void ThreadMain() override {
+ // Wait for the other thread to make Release_Store
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ base::subtle::Acquire_Load(value_);
+ }
+ private:
+ base::subtle::Atomic32 *value_;
+};
+
+void RunInParallel(PlatformThread::Delegate *d1, PlatformThread::Delegate *d2) {
+ PlatformThreadHandle a;
+ PlatformThreadHandle b;
+ PlatformThread::Create(0, d1, &a);
+ PlatformThread::Create(0, d2, &b);
+ PlatformThread::Join(a);
+ PlatformThread::Join(b);
+}
+
+#if defined(THREAD_SANITIZER)
+void DataRace() {
+ bool *shared = new bool(false);
+ TOOLS_SANITY_TEST_CONCURRENT_THREAD thread1(shared), thread2(shared);
+ RunInParallel(&thread1, &thread2);
+ EXPECT_TRUE(*shared);
+ delete shared;
+ // We're in a death test - crash.
+ CHECK(0);
+}
+#endif
+
+} // namespace
+
+#if defined(THREAD_SANITIZER)
+// A data race detector should report an error in this test.
+TEST(ToolsSanityTest, DataRace) {
+ // The suppression regexp must match that in base/debug/tsan_suppressions.cc.
+ EXPECT_DEATH(DataRace(), "1 race:base/tools_sanity_unittest.cc");
+}
+#endif
+
+TEST(ToolsSanityTest, AnnotateBenignRace) {
+ bool shared = false;
+ ANNOTATE_BENIGN_RACE(&shared, "Intentional race - make sure doesn't show up");
+ TOOLS_SANITY_TEST_CONCURRENT_THREAD thread1(&shared), thread2(&shared);
+ RunInParallel(&thread1, &thread2);
+ EXPECT_TRUE(shared);
+}
+
+TEST(ToolsSanityTest, AtomicsAreIgnored) {
+ base::subtle::Atomic32 shared = 0;
+ ReleaseStoreThread thread1(&shared);
+ AcquireLoadThread thread2(&shared);
+ RunInParallel(&thread1, &thread2);
+ EXPECT_EQ(kMagicValue, shared);
+}
+
+#if BUILDFLAG(CFI_ENFORCEMENT_TRAP)
+#if defined(OS_WIN)
+#define CFI_ERROR_MSG "EXCEPTION_ILLEGAL_INSTRUCTION"
+#elif defined(OS_ANDROID)
+// TODO(pcc): Produce proper stack dumps on Android and test for the correct
+// si_code here.
+#define CFI_ERROR_MSG "^$"
+#else
+#define CFI_ERROR_MSG "ILL_ILLOPN"
+#endif
+#elif BUILDFLAG(CFI_ENFORCEMENT_DIAGNOSTIC)
+#define CFI_ERROR_MSG "runtime error: control flow integrity check"
+#endif // BUILDFLAG(CFI_ENFORCEMENT_TRAP || CFI_ENFORCEMENT_DIAGNOSTIC)
+
+#if defined(CFI_ERROR_MSG)
+class A {
+ public:
+ A(): n_(0) {}
+ virtual void f() { n_++; }
+ protected:
+ int n_;
+};
+
+class B: public A {
+ public:
+ void f() override { n_--; }
+};
+
+class C: public B {
+ public:
+ void f() override { n_ += 2; }
+};
+
+NOINLINE void KillVptrAndCall(A *obj) {
+ *reinterpret_cast<void **>(obj) = 0;
+ obj->f();
+}
+
+TEST(ToolsSanityTest, BadVirtualCallNull) {
+ A a;
+ B b;
+ EXPECT_DEATH({ KillVptrAndCall(&a); KillVptrAndCall(&b); }, CFI_ERROR_MSG);
+}
+
+NOINLINE void OverwriteVptrAndCall(B *obj, A *vptr) {
+ *reinterpret_cast<void **>(obj) = *reinterpret_cast<void **>(vptr);
+ obj->f();
+}
+
+TEST(ToolsSanityTest, BadVirtualCallWrongType) {
+ A a;
+ B b;
+ C c;
+ EXPECT_DEATH({ OverwriteVptrAndCall(&b, &a); OverwriteVptrAndCall(&b, &c); },
+ CFI_ERROR_MSG);
+}
+
+// TODO(pcc): remove CFI_CAST_CHECK, see https://crbug.com/626794.
+#if BUILDFLAG(CFI_CAST_CHECK)
+TEST(ToolsSanityTest, BadDerivedCast) {
+ A a;
+ EXPECT_DEATH((void)(B*)&a, CFI_ERROR_MSG);
+}
+
+TEST(ToolsSanityTest, BadUnrelatedCast) {
+ class A {
+ virtual void f() {}
+ };
+
+ class B {
+ virtual void f() {}
+ };
+
+ A a;
+ EXPECT_DEATH((void)(B*)&a, CFI_ERROR_MSG);
+}
+#endif // BUILDFLAG(CFI_CAST_CHECK)
+
+#endif // CFI_ERROR_MSG
+
+#undef CFI_ERROR_MSG
+#undef MAYBE_AccessesToNewMemory
+#undef MAYBE_AccessesToMallocMemory
+#undef MAYBE_ArrayDeletedWithoutBraces
+#undef MAYBE_SingleElementDeletedWithBraces
+#undef HARMFUL_ACCESS
+#undef HARMFUL_ACCESS_IS_NOOP
+
+} // namespace base