summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/test/nsTestCrasher.cpp
blob: d8de2ed63ecfa3d5042efbf88a28918c8a0a105f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
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__)
}