summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/test/nsTestCrasher.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /toolkit/crashreporter/test/nsTestCrasher.cpp
parentInitial commit. (diff)
downloadthunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz
thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/crashreporter/test/nsTestCrasher.cpp')
-rw-r--r--toolkit/crashreporter/test/nsTestCrasher.cpp364
1 files changed, 364 insertions, 0 deletions
diff --git a/toolkit/crashreporter/test/nsTestCrasher.cpp b/toolkit/crashreporter/test/nsTestCrasher.cpp
new file mode 100644
index 0000000000..d8de2ed63e
--- /dev/null
+++ b/toolkit/crashreporter/test/nsTestCrasher.cpp
@@ -0,0 +1,364 @@
+#include "mozilla/Assertions.h"
+
+#include <stdio.h>
+#include <map>
+
+#include "nscore.h"
+#include "mozilla/Unused.h"
+#include "ExceptionThrower.h"
+
+#ifdef XP_WIN
+# include <malloc.h>
+# include <windows.h>
+#elif defined(XP_MACOSX)
+# include <sys/types.h>
+# include <sys/fcntl.h>
+# include <unistd.h>
+# include <dlfcn.h> // For dlsym()
+// See https://github.com/apple/darwin-xnu/blob/main/bsd/sys/guarded.h
+# define GUARD_CLOSE (1u << 0)
+# define GUARD_DUP (1u << 1)
+# define GUARD_SOCKET_IPC (1u << 2)
+# define GUARD_FILEPORT (1u << 3)
+# define GUARD_WRITE (1u << 4)
+typedef uint64_t guardid_t;
+typedef int (*guarded_open_np_t)(const char*, const guardid_t*, u_int, int,
+ ...);
+#endif
+
+#ifndef XP_WIN
+# include <pthread.h>
+#endif
+
+#ifdef MOZ_PHC
+# include "replace_malloc_bridge.h"
+#endif
+
+/*
+ * This pure virtual call example is from MSDN
+ */
+class A;
+
+void fcn(A*);
+
+class A {
+ public:
+ virtual void f() = 0;
+ A() { fcn(this); }
+};
+
+class B : A {
+ void f() override {}
+
+ public:
+ void use() {}
+};
+
+void fcn(A* p) { p->f(); }
+
+void PureVirtualCall() {
+ // generates a pure virtual function call
+ B b;
+ b.use(); // make sure b's actually used
+}
+
+extern "C" {
+#if XP_WIN && HAVE_64BIT_BUILD && defined(_M_X64) && !defined(__MINGW32__)
+// Implementation in win64unwindInfoTests.asm
+uint64_t x64CrashCFITest_NO_MANS_LAND(uint64_t returnpfn, void*);
+uint64_t x64CrashCFITest_Launcher(uint64_t returnpfn, void* testProc);
+uint64_t x64CrashCFITest_UnknownOpcode(uint64_t returnpfn, void*);
+uint64_t x64CrashCFITest_PUSH_NONVOL(uint64_t returnpfn, void*);
+uint64_t x64CrashCFITest_ALLOC_SMALL(uint64_t returnpfn, void*);
+uint64_t x64CrashCFITest_ALLOC_LARGE(uint64_t returnpfn, void*);
+uint64_t x64CrashCFITest_SAVE_NONVOL(uint64_t returnpfn, void*);
+uint64_t x64CrashCFITest_SAVE_NONVOL_FAR(uint64_t returnpfn, void*);
+uint64_t x64CrashCFITest_SAVE_XMM128(uint64_t returnpfn, void*);
+uint64_t x64CrashCFITest_SAVE_XMM128_FAR(uint64_t returnpfn, void*);
+uint64_t x64CrashCFITest_EPILOG(uint64_t returnpfn, void*);
+uint64_t x64CrashCFITest_EOF(uint64_t returnpfn, void*);
+#endif // XP_WIN && HAVE_64BIT_BUILD && !defined(__MINGW32__)
+}
+
+// Keep these in sync with CrashTestUtils.jsm!
+const int16_t CRASH_INVALID_POINTER_DEREF = 0;
+const int16_t CRASH_PURE_VIRTUAL_CALL = 1;
+const int16_t CRASH_OOM = 3;
+const int16_t CRASH_MOZ_CRASH = 4;
+const int16_t CRASH_ABORT = 5;
+const int16_t CRASH_UNCAUGHT_EXCEPTION = 6;
+#if XP_WIN && HAVE_64BIT_BUILD && defined(_M_X64) && !defined(__MINGW32__)
+const int16_t CRASH_X64CFI_NO_MANS_LAND = 7;
+const int16_t CRASH_X64CFI_LAUNCHER = 8;
+const int16_t CRASH_X64CFI_UNKNOWN_OPCODE = 9;
+const int16_t CRASH_X64CFI_PUSH_NONVOL = 10;
+const int16_t CRASH_X64CFI_ALLOC_SMALL = 11;
+const int16_t CRASH_X64CFI_ALLOC_LARGE = 12;
+const int16_t CRASH_X64CFI_SAVE_NONVOL = 15;
+const int16_t CRASH_X64CFI_SAVE_NONVOL_FAR = 16;
+const int16_t CRASH_X64CFI_SAVE_XMM128 = 17;
+const int16_t CRASH_X64CFI_SAVE_XMM128_FAR = 18;
+const int16_t CRASH_X64CFI_EPILOG = 19;
+const int16_t CRASH_X64CFI_EOF = 20;
+#endif
+const int16_t CRASH_PHC_USE_AFTER_FREE = 21;
+const int16_t CRASH_PHC_DOUBLE_FREE = 22;
+const int16_t CRASH_PHC_BOUNDS_VIOLATION = 23;
+#if XP_WIN
+const int16_t CRASH_HEAP_CORRUPTION = 24;
+#endif
+#ifdef XP_MACOSX
+const int16_t CRASH_EXC_GUARD = 25;
+#endif
+#ifndef XP_WIN
+const int16_t CRASH_STACK_OVERFLOW = 26;
+#endif
+
+#if XP_WIN && HAVE_64BIT_BUILD && defined(_M_X64) && !defined(__MINGW32__)
+
+typedef decltype(&x64CrashCFITest_UnknownOpcode) win64CFITestFnPtr_t;
+
+static std::map<int16_t, win64CFITestFnPtr_t> GetWin64CFITestMap() {
+ std::map<int16_t, win64CFITestFnPtr_t> ret = {
+ {CRASH_X64CFI_NO_MANS_LAND, x64CrashCFITest_NO_MANS_LAND},
+ {CRASH_X64CFI_LAUNCHER, x64CrashCFITest_Launcher},
+ {CRASH_X64CFI_UNKNOWN_OPCODE, x64CrashCFITest_UnknownOpcode},
+ {CRASH_X64CFI_PUSH_NONVOL, x64CrashCFITest_PUSH_NONVOL},
+ {CRASH_X64CFI_ALLOC_SMALL, x64CrashCFITest_ALLOC_SMALL},
+ {CRASH_X64CFI_ALLOC_LARGE, x64CrashCFITest_ALLOC_LARGE},
+ {CRASH_X64CFI_SAVE_NONVOL, x64CrashCFITest_SAVE_NONVOL},
+ {CRASH_X64CFI_SAVE_NONVOL_FAR, x64CrashCFITest_SAVE_NONVOL_FAR},
+ {CRASH_X64CFI_SAVE_XMM128, x64CrashCFITest_SAVE_XMM128},
+ {CRASH_X64CFI_SAVE_XMM128_FAR, x64CrashCFITest_SAVE_XMM128_FAR},
+ {CRASH_X64CFI_EPILOG, x64CrashCFITest_EPILOG},
+ {CRASH_X64CFI_EOF, x64CrashCFITest_EOF}};
+ // ret values point to jump table entries, not the actual function bodies.
+ // Get the correct pointer by calling the function with returnpfn=1
+ for (auto it = ret.begin(); it != ret.end(); ++it) {
+ it->second = (win64CFITestFnPtr_t)it->second(1, nullptr);
+ }
+ return ret;
+}
+
+// This ensures tests have enough committed stack space.
+// Must not be inlined, or the stack space would not be freed for the caller
+// to use.
+void MOZ_NEVER_INLINE ReserveStack() {
+ // We must actually use the memory in some way that the compiler can't
+ // optimize away.
+ static const size_t elements = (1024000 / sizeof(FILETIME)) + 1;
+ FILETIME stackmem[elements];
+ ::GetSystemTimeAsFileTime(&stackmem[0]);
+ ::GetSystemTimeAsFileTime(&stackmem[elements - 1]);
+}
+
+#endif // XP_WIN && HAVE_64BIT_BUILD
+
+#ifdef MOZ_PHC
+uint8_t* GetPHCAllocation(size_t aSize) {
+ // A crude but effective way to get a PHC allocation.
+ for (int i = 0; i < 2000000; i++) {
+ uint8_t* p = (uint8_t*)malloc(aSize);
+ if (ReplaceMalloc::IsPHCAllocation(p, nullptr)) {
+ return p;
+ }
+ free(p);
+ }
+ // This failure doesn't seem to occur in practice...
+ MOZ_CRASH("failed to get a PHC allocation");
+}
+#endif
+
+#ifndef XP_WIN
+static int64_t recurse(int64_t aRandom) {
+ char buff[256] = {};
+ int64_t result = aRandom;
+
+ strncpy(buff, "This is gibberish", sizeof(buff));
+
+ for (auto& c : buff) {
+ result += c;
+ }
+
+ if (result == 0) {
+ return result;
+ }
+
+ return recurse(result) + 1;
+}
+
+static void* overflow_stack(void* aInput) {
+ int64_t result = recurse(*((int64_t*)(aInput)));
+
+ return (void*)result;
+}
+#endif // XP_WIN
+
+extern "C" NS_EXPORT void Crash(int16_t how) {
+ switch (how) {
+ case CRASH_INVALID_POINTER_DEREF: {
+ volatile int* foo = (int*)0x42;
+ *foo = 0;
+ // not reached
+ break;
+ }
+ case CRASH_PURE_VIRTUAL_CALL: {
+ PureVirtualCall();
+ // not reached
+ break;
+ }
+ case CRASH_OOM: {
+ mozilla::Unused << moz_xmalloc((size_t)-1);
+ mozilla::Unused << moz_xmalloc((size_t)-1);
+ mozilla::Unused << moz_xmalloc((size_t)-1);
+ break;
+ }
+ case CRASH_MOZ_CRASH: {
+ MOZ_CRASH();
+ break;
+ }
+ case CRASH_ABORT: {
+ abort();
+ break;
+ }
+ case CRASH_UNCAUGHT_EXCEPTION: {
+ ThrowException();
+ break;
+ }
+#if XP_WIN && HAVE_64BIT_BUILD && defined(_M_X64) && !defined(__MINGW32__)
+ case CRASH_X64CFI_UNKNOWN_OPCODE:
+ case CRASH_X64CFI_PUSH_NONVOL:
+ case CRASH_X64CFI_ALLOC_SMALL:
+ case CRASH_X64CFI_ALLOC_LARGE:
+ case CRASH_X64CFI_SAVE_NONVOL:
+ case CRASH_X64CFI_SAVE_NONVOL_FAR:
+ case CRASH_X64CFI_SAVE_XMM128:
+ case CRASH_X64CFI_SAVE_XMM128_FAR:
+ case CRASH_X64CFI_EPILOG: {
+ auto m = GetWin64CFITestMap();
+ if (m.find(how) == m.end()) {
+ break;
+ }
+ auto pfnTest = m[how];
+ auto pfnLauncher = m[CRASH_X64CFI_LAUNCHER];
+ ReserveStack();
+ pfnLauncher(0, reinterpret_cast<void*>(pfnTest));
+ break;
+ }
+#endif // XP_WIN && HAVE_64BIT_BUILD && !defined(__MINGW32__)
+#ifdef MOZ_PHC
+ case CRASH_PHC_USE_AFTER_FREE: {
+ // Do a UAF, triggering a crash.
+ uint8_t* p = GetPHCAllocation(32);
+ free(p);
+ p[0] = 0;
+ // not reached
+ }
+ case CRASH_PHC_DOUBLE_FREE: {
+ // Do a double free, triggering a crash.
+ uint8_t* p = GetPHCAllocation(64);
+ free(p);
+ free(p);
+ // not reached
+ }
+ case CRASH_PHC_BOUNDS_VIOLATION: {
+ // Do a bounds violation, triggering a crash.
+ uint8_t* p = GetPHCAllocation(96);
+ p[96] = 0;
+ // not reached
+ }
+#endif
+#if XP_WIN
+ case CRASH_HEAP_CORRUPTION: {
+ // We override the HeapFree() function in mozglue so that we can force
+ // the code calling it to use our allocator instead of the Windows one.
+ // Since we need to call the real HeapFree() we get its pointer directly.
+ HMODULE kernel32 = LoadLibraryW(L"Kernel32.dll");
+ if (kernel32) {
+ typedef BOOL (*HeapFreeT)(HANDLE, DWORD, LPVOID);
+ HeapFreeT heapFree = (HeapFreeT)GetProcAddress(kernel32, "HeapFree");
+ if (heapFree) {
+ HANDLE heap = GetProcessHeap();
+ LPVOID badPointer = (LPVOID)3;
+ heapFree(heap, 0, badPointer);
+ break; // This should be unreachable
+ }
+ }
+ }
+#endif // XP_WIN
+#ifdef XP_MACOSX
+ case CRASH_EXC_GUARD: {
+ guarded_open_np_t dl_guarded_open_np;
+ void* kernellib =
+ (void*)dlopen("/usr/lib/system/libsystem_kernel.dylib", RTLD_GLOBAL);
+ dl_guarded_open_np =
+ (guarded_open_np_t)dlsym(kernellib, "guarded_open_np");
+ const guardid_t guard = 0x123456789ABCDEFULL;
+ // Guard the file descriptor against regular close() calls
+ int fd = dl_guarded_open_np(
+ "/tmp/try.txt", &guard,
+ GUARD_CLOSE | GUARD_DUP | GUARD_SOCKET_IPC | GUARD_FILEPORT,
+ O_CREAT | O_CLOEXEC | O_RDWR, 0666);
+
+ if (fd != -1) {
+ close(fd);
+ // not reached
+ }
+ }
+#endif // XP_MACOSX
+#ifndef XP_WIN
+ case CRASH_STACK_OVERFLOW: {
+ pthread_t thread_id;
+ int64_t data = 1337;
+ int rv = pthread_create(&thread_id, nullptr, overflow_stack, &data);
+ if (!rv) {
+ pthread_join(thread_id, nullptr);
+ }
+
+ break; // This should be unreachable
+ }
+#endif // XP_WIN
+ default:
+ break;
+ }
+}
+
+char testData[32];
+
+extern "C" NS_EXPORT uint64_t SaveAppMemory() {
+ for (size_t i = 0; i < sizeof(testData); i++) testData[i] = i;
+
+ FILE* fp = fopen("crash-addr", "w");
+ if (!fp) return 0;
+ fprintf(fp, "%p\n", (void*)testData);
+ fclose(fp);
+
+ return (int64_t)testData;
+}
+
+#ifdef XP_WIN
+static LONG WINAPI HandleException(EXCEPTION_POINTERS* exinfo) {
+ TerminateProcess(GetCurrentProcess(), 0);
+ return 0;
+}
+
+extern "C" NS_EXPORT void TryOverrideExceptionHandler() {
+ SetUnhandledExceptionFilter(HandleException);
+}
+#endif
+
+extern "C" NS_EXPORT uint32_t GetWin64CFITestFnAddrOffset(int16_t fnid) {
+#if XP_WIN && HAVE_64BIT_BUILD && defined(_M_X64) && !defined(__MINGW32__)
+ // fnid uses the same constants as Crash().
+ // Returns the RVA of the requested function.
+ // Returns 0 on failure.
+ auto m = GetWin64CFITestMap();
+ if (m.find(fnid) == m.end()) {
+ return 0;
+ }
+ uint64_t moduleBase = (uint64_t)GetModuleHandleW(L"testcrasher.dll");
+ return ((uint64_t)m[fnid]) - moduleBase;
+#else
+ return 0;
+#endif // XP_WIN && HAVE_64BIT_BUILD && !defined(__MINGW32__)
+}