Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtestcrashhandler_unix.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// Copyright (C) 2024 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include <QtTest/private/qtestcrashhandler_p.h>
6
7#include <QtCore/qbytearray.h>
8#include <QtCore/qstring.h>
9#include <QtCore/private/qcore_unix_p.h>
10
11#include <QtTest/qtestcase.h>
12#include <QtTest/private/qtestlog_p.h>
13
14#if !defined(Q_OS_INTEGRITY) || __GHS_VERSION_NUMBER > 202014
15# include <charconv>
16#else
17// Broken implementation, causes link failures just by #include'ing!
18# undef __cpp_lib_to_chars // in case <version> was included
19#endif
20#include <string_view>
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include <errno.h>
27#if __has_include(<paths.h>)
28# include <paths.h>
29#endif
30#include <signal.h>
31#include <time.h>
32#include <sys/mman.h>
33#include <sys/uio.h>
34#include <sys/wait.h>
35#include <unistd.h>
36# if !defined(Q_OS_INTEGRITY)
37# include <sys/resource.h>
38# endif
39# if __has_include(<sys/ucontext.h>)
40# include <sys/ucontext.h>
41# elif __has_include(<ucontext.h>)
42# include <ucontext.h>
43# else
44using ucontext_t = void;
45# endif
46
47#if defined(Q_OS_MACOS)
48#include <QtCore/private/qcore_mac_p.h>
49#include <QtTest/private/qtestutil_macos_p.h>
50
51#include <IOKit/pwr_mgt/IOPMLib.h>
52#include <mach/task.h>
53#include <mach/mach_init.h>
54#include <CoreFoundation/CFPreferences.h>
55
56#define CSR_ALLOW_UNRESTRICTED_FS (1 << 1)
57#endif
58
59#if defined(Q_OS_LINUX)
60#include <sys/prctl.h>
61#include <sys/types.h>
62#include <fcntl.h>
63#endif
64
65#if defined(Q_OS_WASM)
66#include <emscripten.h>
67#endif
68
69#ifndef _PATH_DEFPATH
70# define _PATH_DEFPATH "/usr/bin:/bin"
71#endif
72#ifndef SIGSTKSZ
73# define SIGSTKSZ 0 /* we have code to set the minimum */
74#endif
75#ifndef SA_RESETHAND
76# define SA_RESETHAND 0
77#endif
78
79QT_BEGIN_NAMESPACE
80
81using namespace Qt::StringLiterals;
82
83# define OUR_SIGNALS(F)
84 F(HUP)
85 F(INT)
86 F(QUIT)
87 F(ABRT)
88 F(ILL)
89 F(BUS)
90 F(FPE)
91 F(SEGV)
92 F(PIPE)
93 F(TERM)
94 /**/
95# define CASE_LABEL(S) case SIG ## S: return QT_STRINGIFY(S);
96# define ENUMERATE_SIGNALS(S) SIG ## S,
97static const char *signalName(int signum) noexcept
98{
99 switch (signum) {
100 OUR_SIGNALS(CASE_LABEL)
101 }
102
103# if defined(__GLIBC_MINOR__) && (__GLIBC_MINOR__ >= 32 || __GLIBC__ > 2)
104 // get the other signal names from glibc 2.32
105 // (accessing the sys_sigabbrev variable causes linker warnings)
106 if (const char *p = sigabbrev_np(signum))
107 return p;
108# endif
109 return "???";
110}
111static constexpr std::array fatalSignals = {
112 OUR_SIGNALS(ENUMERATE_SIGNALS)
113};
114# undef CASE_LABEL
115# undef ENUMERATE_SIGNALS
116
117static constexpr std::array crashingSignals = {
118 // Crash signals are special, because if we return from the handler
119 // without adjusting the machine state, the same instruction that
120 // originally caused the crash will get re-executed and will thus cause
121 // the same crash again. This is useful if our parent process logs the
122 // exit result or if core dumps are enabled: the core file will point
123 // to the actual instruction that crashed.
124 SIGILL, SIGBUS, SIGFPE, SIGSEGV
125};
126using OldActionsArray = std::array<struct sigaction, fatalSignals.size()>;
127
128template <typename... Args> static ssize_t writeToStderr(Args &&... args)
129{
130 auto makeIovec = [](std::string_view arg) {
131 struct iovec r = {};
132 r.iov_base = const_cast<char *>(arg.data());
133 r.iov_len = arg.size();
134 return r;
135 };
136 struct iovec vec[] = { makeIovec(std::forward<Args>(args))... };
137 return ::writev(STDERR_FILENO, vec, std::size(vec));
138}
139
140namespace {
141// async-signal-safe conversion from int to string
142struct AsyncSafeIntBuffer
143{
144 // digits10 + 1 for all possible digits
145 // +1 for the sign
146 // +1 for the terminating null
147 static constexpr int Digits10 = std::numeric_limits<int>::digits10 + 3;
148 std::array<char, Digits10> array;
149 constexpr AsyncSafeIntBuffer() : array{} {} // initializes array
150 AsyncSafeIntBuffer(Qt::Initialization) {} // leaves array uninitialized
151};
152
153std::string_view asyncSafeToString(int n, AsyncSafeIntBuffer &&result = Qt::Uninitialized)
154{
155 char *ptr = result.array.data();
156 if (false) {
157#ifdef __cpp_lib_to_chars
158 } else if (auto r = std::to_chars(ptr, ptr + result.array.size(), n, 10); r.ec == std::errc{}) {
159 ptr = r.ptr;
160#endif
161 } else {
162 // handle the sign
163 if (n < 0) {
164 *ptr++ = '-';
165 n = -n;
166 }
167
168 // find the highest power of the base that is less than this number
169 static constexpr int StartingDivider = ([]() {
170 int divider = 1;
171 for (int i = 0; i < std::numeric_limits<int>::digits10; ++i)
172 divider *= 10;
173 return divider;
174 }());
175 int divider = StartingDivider;
176 while (divider && n < divider)
177 divider /= 10;
178
179 // now convert to string
180 while (divider > 1) {
181 int quot = n / divider;
182 n = n % divider;
183 divider /= 10;
184 *ptr++ = quot + '0';
185 }
186 *ptr++ = n + '0';
187 }
188
189#ifndef QT_NO_DEBUG
190 // this isn't necessary, it just helps in the debugger
191 *ptr = '\0';
192#endif
193 return std::string_view(result.array.data(), ptr - result.array.data());
194};
195
196std::string_view asyncSafeToHexString(quintptr u, char *ptr)
197{
198 // We format with leading zeroes so the output is of fixed length.
199 // Formatting to shorter is more complex and unnecessary here (unlike
200 // decimals above).
201 int shift = sizeof(quintptr) * 8 - 4;
202 ptr[0] = '0';
203 ptr[1] = 'x';
204 for (size_t i = 0; i < sizeof(quintptr) * 2; ++i, shift -= 4)
205 ptr[i + 2] = QtMiscUtils::toHexLower(u >> shift);
206
207 return std::string_view(ptr, sizeof(quintptr) * 2 + 2);
208}
209} // unnamed namespace
210
211namespace QTest {
212namespace CrashHandler {
213Q_CONSTINIT static OldActionsArray oldActions {};
214static bool pauseOnCrash = false;
215
216static void actionHandler(int signum, siginfo_t *info, void * /* ucontext */);
217
219{
220#if defined(Q_OS_LINUX)
221 int fd = open("/proc/self/status", O_RDONLY);
222 if (fd == -1)
223 return false;
224 char buffer[2048];
225 ssize_t size = read(fd, buffer, sizeof(buffer) - 1);
226 if (size == -1) {
227 close(fd);
228 return false;
229 }
230 buffer[size] = 0;
231 const char tracerPidToken[] = "\nTracerPid:";
232 char *tracerPid = strstr(buffer, tracerPidToken);
233 if (!tracerPid) {
234 close(fd);
235 return false;
236 }
237 tracerPid += sizeof(tracerPidToken);
238 long int pid = strtol(tracerPid, &tracerPid, 10);
239 close(fd);
240 return pid != 0;
241#elif defined(Q_OS_MACOS)
242 // Check if there is an exception handler for the process:
243 mach_msg_type_number_t portCount = 0;
244 exception_mask_t masks[EXC_TYPES_COUNT];
245 mach_port_t ports[EXC_TYPES_COUNT];
246 exception_behavior_t behaviors[EXC_TYPES_COUNT];
247 thread_state_flavor_t flavors[EXC_TYPES_COUNT];
248 exception_mask_t mask = EXC_MASK_ALL & ~(EXC_MASK_RESOURCE | EXC_MASK_GUARD);
249 kern_return_t result = task_get_exception_ports(mach_task_self(), mask, masks, &portCount,
250 ports, behaviors, flavors);
251 if (result == KERN_SUCCESS) {
252 for (mach_msg_type_number_t portIndex = 0; portIndex < portCount; ++portIndex) {
253 if (MACH_PORT_VALID(ports[portIndex])) {
254 return true;
255 }
256 }
257 }
258 return false;
259#else
260 // TODO
261 return false;
262#endif
263}
264
266{
267#if defined(Q_OS_MACOS)
268 return QTestPrivate::macCrashReporterWillShowDialog();
269#else
270 return false;
271#endif
272}
273
275{
276#ifdef RLIMIT_CORE
277 bool ok = false;
278 const int disableCoreDump = qEnvironmentVariableIntValue("QTEST_DISABLE_CORE_DUMP", &ok);
279 if (ok && disableCoreDump) {
280 struct rlimit limit;
281 limit.rlim_cur = 0;
282 limit.rlim_max = 0;
283 if (setrlimit(RLIMIT_CORE, &limit) != 0)
284 qWarning("Failed to disable core dumps: %d", errno);
285 }
286#endif
287}
288
291{
292
293 bool ok = false;
294 const int disableStackDump = qEnvironmentVariableIntValue("QTEST_DISABLE_STACK_DUMP", &ok);
295 if (ok && disableStackDump)
296 return;
297
299 return;
300
301#if defined(Q_OS_MACOS)
302 // Try to handle https://github.com/llvm/llvm-project/issues/53254,
303 // where LLDB will hang and fail to provide a valid stack trace.
304# if defined(Q_PROCESSOR_ARM)
305 return;
306# else
307 std::optional<uint32_t> sipConfiguration = qt_mac_sipConfiguration();
308 if (!sipConfiguration || !(*sipConfiguration & CSR_ALLOW_UNRESTRICTED_FS))
309 return;
310# endif
311#endif
312
313 // like QStandardPaths::findExecutable(), but simpler
314 auto hasExecutable = [](const char *execname) {
315 std::string candidate;
316 std::string path;
317 if (const char *p = getenv("PATH"); p && *p)
318 path = p;
319 else
320 path = _PATH_DEFPATH;
321 char *saveptr = nullptr;
322 for (const char *p = strtok_r(&path[0], ":", &saveptr); p;
323 p = strtok_r(nullptr, ":", &saveptr)) {
324 candidate = p;
325 candidate += '/';
326 candidate += execname;
327 if (QT_ACCESS(candidate.data(), X_OK) == 0)
328 return true;
329 }
330 return false;
331 };
332
333 static constexpr DebuggerProgram debuggerSearchOrder[] = {
334# if defined(Q_OS_QNX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
335 Gdb, Lldb
336# else
337 Lldb, Gdb
338# endif
339 };
340 for (DebuggerProgram candidate : debuggerSearchOrder) {
341 switch (candidate) {
342 case None:
343 Q_UNREACHABLE();
344 break;
345 case Gdb:
346 if (hasExecutable("gdb")) {
347 debugger = Gdb;
348 return;
349 }
350 break;
351 case Lldb:
352 if (hasExecutable("lldb")) {
353 debugger = Lldb;
354 return;
355 }
356 break;
357 }
358 }
359}
360
362{
363 const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
364 const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
365 const char *const name = QTest::currentTestFunction();
366 writeToStderr("\n ", name ? name : "[Non-test]",
367 " function time: ", asyncSafeToString(msecsFunctionTime),
368 "ms, total time: ", asyncSafeToString(msecsTotalTime), "ms\n");
369}
370
371static quintptr getProgramCounter(void *ucontext)
372{
373 quintptr pc = 0;
374 if ([[maybe_unused]] auto ctx = static_cast<ucontext_t *>(ucontext)) {
375#if 0 // keep the list below alphabetical
376
377#elif defined(Q_OS_DARWIN) && defined(Q_PROCESSOR_ARM_64)
378 pc = arm_thread_state64_get_pc(ctx->uc_mcontext->__ss);
379#elif defined(Q_OS_DARWIN) && defined(Q_PROCESSOR_X86_64)
380 pc = ctx->uc_mcontext->__ss.__rip;
381
382#elif defined(Q_OS_FREEBSD) && defined(Q_PROCESSOR_X86_32)
383 pc = ctx->uc_mcontext.mc_eip;
384#elif defined(Q_OS_FREEBSD) && defined(Q_PROCESSOR_X86_64)
385 pc = ctx->uc_mcontext.mc_rip;
386
387#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_32)
388 // pc = ctx->uc_mcontext.arm_pc; // untested
389#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_64)
390 pc = ctx->uc_mcontext.pc;
391#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_MIPS)
392 // pc = ctx->uc_mcontext.pc; // untested
393#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_LOONGARCH)
394 // pc = ctx->uc_mcontext.__pc; // untested
395#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_POWER_32)
396 // pc = ctx->uc_mcontext.uc_regs->gregs[PT_NIP]; // untested
397#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_POWER_64)
398 // pc = ctx->uc_mcontext.gregs[PT_NIP]; // untested
399#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_RISCV)
400 // pc = ctx->uc_mcontext.__gregs[REG_PC]; // untested
401#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_X86_32)
402 pc = ctx->uc_mcontext.gregs[REG_EIP];
403#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_X86_64)
404 pc = ctx->uc_mcontext.gregs[REG_RIP];
405#endif
406 }
407 return pc;
408}
409
410void generateStackTrace(quintptr ip)
411{
412 if (debugger == None || alreadyDebugging())
413 return;
414
415# if defined(Q_OS_LINUX) && defined(PR_SET_PTRACER)
416 // allow ourselves to be debugged
417 (void) prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
418# endif
419
420# if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS)
421 writeToStderr("\n=== Stack trace ===\n");
422
423 // execlp() requires null-termination, so call the default constructor
424 AsyncSafeIntBuffer pidbuffer;
425 asyncSafeToString(getpid(), std::move(pidbuffer));
426
427 // Note: POSIX.1-2001 still has fork() in the list of async-safe functions,
428 // but in a future edition, it might be removed. It would be safer to wake
429 // up a babysitter thread to launch the debugger.
430 pid_t pid = fork();
431 if (pid == 0) {
432 // child process
433 (void) dup2(STDERR_FILENO, STDOUT_FILENO); // redirect stdout to stderr
434
435 // disassemble the crashing instruction, if known
436 // (syntax is the same for gdb and lldb)
437 char disasmInstr[sizeof("x/i 0x") + sizeof(ip) * 2] = {}; // zero-init for terminator
438 if (ip) {
439 strcpy(disasmInstr, "x/i ");
440 asyncSafeToHexString(ip, disasmInstr + strlen(disasmInstr));
441 }
442
443 struct Args {
444 std::array<const char *, 16> argv;
445 int count = 0;
446 Args &operator<<(const char *arg)
447 {
448 Q_ASSERT(count < int(argv.size()));
449 argv[count++] = arg;
450 return *this;
451 }
452 operator char **() const { return const_cast<char **>(argv.data()); }
453 } argv;
454
455 switch (debugger) {
456 case None:
457 Q_UNREACHABLE();
458 break;
459 case Gdb:
460 argv << "gdb" << "--nx" << "--batch";
461 if (ip)
462 argv << "-ex" << disasmInstr;
463 argv << "-ex" << "thread apply all bt"
464 << "-ex" << "printf \"\\n\""
465 << "-ex" << "info proc mappings"
466 << "--pid";
467 break;
468 case Lldb:
469 argv << "lldb" << "--no-lldbinit" << "--batch";
470 if (ip)
471 argv << "-o" << disasmInstr;
472 argv << "-o" << "bt all"
473 << "--attach-pid";
474 break;
475 }
476 if (argv.count) {
477 argv << pidbuffer.array.data() << nullptr;
478 execvp(argv.argv[0], argv);
479 }
480 _exit(1);
481 } else if (pid < 0) {
482 writeToStderr("Failed to start debugger.\n");
483 } else {
484 int ret;
485 QT_EINTR_LOOP(ret, waitpid(pid, nullptr, 0));
486 }
487
488 writeToStderr("=== End of stack trace ===\n");
489# else
490 Q_UNUSED(ip);
491# endif // !Q_OS_INTEGRITY && !Q_OS_VXWORKS
492}
493
494#ifndef Q_OS_WASM // no signal handling for WASM
496{
497 // Block most Unix signals so the WatchDog thread won't be called when
498 // external signals are delivered, thus avoiding interfering with the test
499 sigset_t set;
500 sigfillset(&set);
501
502 // we allow the crashing signals, in case we have bugs
503 for (int signo : fatalSignals)
504 sigdelset(&set, signo);
505
506 pthread_sigmask(SIG_BLOCK, &set, nullptr);
507}
508
509static std::string_view unixSignalCodeToName(int signo, int code) noexcept
510{
511 switch (signo) {
512 case SIGFPE:
513 switch (code) {
514#ifdef FPE_INTDIV
515 case FPE_INTDIV: return "FPE_INTDIV"; // Integer divide by zero.
516#endif
517#ifdef FPE_INTOVF
518 case FPE_INTOVF: return "FPE_INTOVF"; // Integer overflow.
519#endif
520#ifdef FPE_FLTDIV
521 case FPE_FLTDIV: return "FPE_FLTDIV"; // Floating point divide by zero.
522#endif
523#ifdef FPE_FLTOVF
524 case FPE_FLTOVF: return "FPE_FLTOVF"; // Floating point overflow.
525#endif
526#ifdef FPE_FLTUND
527 case FPE_FLTUND: return "FPE_FLTUND"; // Floating point underflow.
528#endif
529#ifdef FPE_FLTRES
530 case FPE_FLTRES: return "FPE_FLTRES"; // Floating point inexact result.
531#endif
532#ifdef FPE_FLTINV
533 case FPE_FLTINV: return "FPE_FLTINV"; // Floating point invalid operation.
534#endif
535#ifdef FPE_FLTSUB
536 case FPE_FLTSUB: return "FPE_FLTSUB"; // Subscript out of range.
537#endif
538#ifdef FPE_FLTUNK
539 case FPE_FLTUNK: return "FPE_FLTUNK"; // Undiagnosed floating-point exception.
540#endif
541#ifdef FPE_CONDTRAP
542 case FPE_CONDTRAP: return "FPE_CONDTRAP"; // Trap on condition.
543#endif
544 }
545 break;
546
547 case SIGILL:
548 switch (code) {
549#ifdef ILL_ILLOPC
550 case ILL_ILLOPC: return "ILL_ILLOPC"; // Illegal opcode.
551#endif
552#ifdef ILL_ILLOPN
553 case ILL_ILLOPN: return "ILL_ILLOPN"; // Illegal operand.
554#endif
555#ifdef ILL_ILLADR
556 case ILL_ILLADR: return "ILL_ILLADR"; // Illegal addressing mode.
557#endif
558#ifdef ILL_ILLTRP
559 case ILL_ILLTRP: return "ILL_ILLTRP"; // Illegal trap.
560#endif
561#ifdef ILL_PRVOPC
562 case ILL_PRVOPC: return "ILL_PRVOPC"; // Privileged opcode.
563#endif
564#ifdef ILL_PRVREG
565 case ILL_PRVREG: return "ILL_PRVREG"; // Privileged register.
566#endif
567#ifdef ILL_COPROC
568 case ILL_COPROC: return "ILL_COPROC"; // Coprocessor error.
569#endif
570#ifdef ILL_BADSTK
571 case ILL_BADSTK: return "ILL_BADSTK"; // Internal stack error.
572#endif
573#ifdef ILL_BADIADDR
574 case ILL_BADIADDR: return "ILL_BADIADDR"; // Unimplemented instruction address.
575#endif
576 }
577 break;
578
579 case SIGSEGV:
580 switch (code) {
581#ifdef SEGV_MAPERR
582 case SEGV_MAPERR: return "SEGV_MAPERR"; // Address not mapped to object.
583#endif
584#ifdef SEGV_ACCERR
585 case SEGV_ACCERR: return "SEGV_ACCERR"; // Invalid permissions for mapped object.
586#endif
587#ifdef SEGV_BNDERR
588 // Intel MPX - deprecated
589 case SEGV_BNDERR: return "SEGV_BNDERR"; // Bounds checking failure.
590#endif
591#ifdef SEGV_PKUERR
592 // Intel PKRU
593 case SEGV_PKUERR: return "SEGV_PKUERR"; // Protection key checking failure.
594#endif
595#ifdef Q_PROCESSOR_SPARC
596 // these seem to be Sparc-specific on Linux
597# ifdef SEGV_ACCADI
598 case SEGV_ACCADI: return "SEGV_ACCADI"; // ADI not enabled for mapped object.
599# endif
600# ifdef SEGV_ADIDERR
601 case SEGV_ADIDERR: return "SEGV_ADIDERR"; // Disrupting MCD error.
602# endif
603# ifdef SEGV_ADIPERR
604 case SEGV_ADIPERR: return "SEGV_ADIPERR"; // Precise MCD exception.
605# endif
606#endif
607#ifdef Q_PROCESSOR_ARM
608# ifdef SEGV_MTEAERR
609 case SEGV_MTEAERR: return "SEGV_MTEAERR"; // Asynchronous ARM MTE error.
610# endif
611# ifdef SEGV_MTESERR
612 case SEGV_MTESERR: return "SEGV_MTESERR"; // Synchronous ARM MTE exception.
613# endif
614#endif
615#ifdef SEGV_CPERR
616 // seen on both AArch64 and x86 Linux
617 case SEGV_CPERR: return "SEGV_CPERR"; // Control protection fault
618#endif
619 }
620 break;
621
622 case SIGBUS:
623 switch (code) {
624#ifdef BUS_ADRALN
625 case BUS_ADRALN: return "BUS_ADRALN"; // Invalid address alignment.
626#endif
627#ifdef BUS_ADRERR
628 case BUS_ADRERR: return "BUS_ADRERR"; // Non-existant physical address.
629#endif
630#ifdef BUS_OBJERR
631 case BUS_OBJERR: return "BUS_OBJERR"; // Object specific hardware error.
632#endif
633#ifdef BUS_MCEERR_AR
634 case BUS_MCEERR_AR: return "BUS_MCEERR_AR"; // Hardware memory error: action required.
635#endif
636#ifdef BUS_MCEERR_AO
637 case BUS_MCEERR_AO: return "BUS_MCEERR_AO"; // Hardware memory error: action optional.
638#endif
639 }
640 break;
641 }
642
643 return {};
644}
645
646template <typename T> static
647 std::enable_if_t<sizeof(std::declval<T>().si_pid) + sizeof(std::declval<T>().si_uid) >= 1>
649{
650 writeToStderr(" sent by PID ", asyncSafeToString(info->si_pid),
651 " UID ", asyncSafeToString(info->si_uid));
652}
653[[maybe_unused]] static void printSentSignalInfo(...) {}
654
655template <typename T> static std::enable_if_t<sizeof(std::declval<T>().si_addr) >= 1>
656printCrashingSignalInfo(T *info, quintptr pc)
657{
658 using HexString = std::array<char, sizeof(quintptr) * 2 + 2>;
659 auto toHexString = [](quintptr u, HexString &&r = {}) {
660 return asyncSafeToHexString(u, r.data());
661 };
662
663 std::string_view name = unixSignalCodeToName(info->si_signo, info->si_code);
664 writeToStderr(", code ", name.size() ? name : asyncSafeToString(info->si_code));
665 if (pc)
666 writeToStderr(", at instruction address ", toHexString(pc));
667 writeToStderr(", accessing address ", toHexString(quintptr(info->si_addr)));
668}
670
671[[maybe_unused]] static void regularHandler(int signum)
672{
673 actionHandler(signum, nullptr, nullptr);
674}
675
677{
678 pauseOnCrash = qEnvironmentVariableIsSet("QTEST_PAUSE_ON_CRASH");
679 struct sigaction act;
680 memset(&act, 0, sizeof(act));
681 act.sa_handler = SIG_DFL;
683
684 // Remove the handler after it is invoked.
686
687# ifdef SA_SIGINFO
688 act.sa_flags |= SA_SIGINFO;
689 act.sa_sigaction = actionHandler;
690# else
692# endif
693
694 // Block all fatal signals in our signal handler so we don't try to close
695 // the testlog twice.
697 for (int signal : fatalSignals)
699
700 for (size_t i = 0; i < fatalSignals.size(); ++i)
702}
703
705{
706 // Restore the default signal handlers in place of ours.
707 // If ours has been replaced, leave the replacement alone.
708 auto isOurs = [](const struct sigaction &old) {
709# ifdef SA_SIGINFO
710 return (old.sa_flags & SA_SIGINFO) && old.sa_sigaction == actionHandler;
711# else
712 return old.sa_handler == regularHandler;
713# endif
714 };
715 struct sigaction action;
716
717 for (size_t i = 0; i < fatalSignals.size(); ++i) {
718 struct sigaction &act = oldActions[i];
719 if (sigaction(fatalSignals[i], nullptr, &action))
720 continue; // Failed to query present handler
721 if (action.sa_flags == 0 && action.sa_handler == SIG_DFL)
722 continue; // Already the default
723 if (isOurs(action))
724 sigaction(fatalSignals[i], &act, nullptr);
725 }
726
728}
729
730static auto alternateStackSize() noexcept
731{
732 struct R { size_t size, pageSize; };
733 static constexpr size_t MinStackSize = 32 * 1024;
734 size_t pageSize = sysconf(_SC_PAGESIZE);
735 size_t size = SIGSTKSZ;
736 if (size < MinStackSize) {
737 size = MinStackSize;
738 } else {
739 // round up to a page
740 size = (size + pageSize - 1) & -pageSize;
741 }
742
743 return R{ size + pageSize, pageSize };
744}
745
747{
748 // tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as
749 // unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h)
750# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
751 // Let the signal handlers use an alternate stack
752 // This is necessary if SIGSEGV is to catch a stack overflow
753 auto r = alternateStackSize();
754 int flags = MAP_PRIVATE | MAP_ANONYMOUS;
755# ifdef MAP_STACK
756 flags |= MAP_STACK;
757# endif
758 alternateStackBase = mmap(nullptr, r.size, PROT_READ | PROT_WRITE, flags, -1, 0);
760 return 0;
761
762 // mark the bottom page inaccessible, to catch a handler stack overflow
763 (void) mprotect(alternateStackBase, r.pageSize, PROT_NONE);
764
766 stack.ss_flags = 0;
768 stack.ss_sp = static_cast<char *>(alternateStackBase) + r.pageSize;
769 sigaltstack(&stack, nullptr);
770 return SA_ONSTACK;
771# else
772 return 0;
773# endif
774}
775
777{
778# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
780 stack_t stack = {};
781 stack.ss_flags = SS_DISABLE;
782 sigaltstack(&stack, nullptr);
784 }
785# endif
786}
787
788void actionHandler(int signum, siginfo_t *info, void *ucontext)
789{
790 writeToStderr("Received signal ", asyncSafeToString(signum),
791 " (SIG", signalName(signum), ")");
792
793 quintptr pc = 0;
794 bool isCrashingSignal =
795 std::find(crashingSignals.begin(), crashingSignals.end(), signum) != crashingSignals.end();
796 if (isCrashingSignal && (!info || info->si_code <= 0))
797 isCrashingSignal = false; // wasn't sent by the kernel, so it's not really a crash
798 if (isCrashingSignal)
799 printCrashingSignalInfo(info, (pc = getProgramCounter(ucontext)));
800 else if (info && (info->si_code == SI_USER || info->si_code == SI_QUEUE))
802
804 if (signum != SIGINT) {
805 generateStackTrace(pc);
806 if (pauseOnCrash) {
807 writeToStderr("Pausing process ", asyncSafeToString(getpid()),
808 " for debugging\n");
809 raise(SIGSTOP);
810 }
811 }
812
813 // chain back to the previous handler, if any
814 for (size_t i = 0; i < fatalSignals.size(); ++i) {
815 struct sigaction &act = oldActions[i];
816 if (signum != fatalSignals[i])
817 continue;
818
819 // restore the handler (if SA_RESETHAND hasn't done the job for us)
820 if (SA_RESETHAND == 0 || act.sa_handler != SIG_DFL || act.sa_flags)
821 (void) sigaction(signum, &act, nullptr);
822
823 if (!isCrashingSignal)
824 raise(signum);
825
826 // signal is blocked, so it'll be delivered when we return
827 return;
828 }
829
830 // we shouldn't reach here!
831 std::abort();
832}
833#endif // !defined(Q_OS_WASM)
834
835} // namespace CrashHandler
836} // namespace QTest
837
838QT_END_NAMESPACE
static std::enable_if_t< sizeof(std::declval< T >().si_addr) >=1 > printCrashingSignalInfo(T *info, quintptr pc)
static quintptr getProgramCounter(void *ucontext)
static void regularHandler(int signum)
static std::string_view unixSignalCodeToName(int signo, int code) noexcept
static auto alternateStackSize() noexcept
static void printCrashingSignalInfo(...)
static std::enable_if_t< sizeof(std::declval< T >().si_pid)+sizeof(std::declval< T >().si_uid) >=1 > printSentSignalInfo(T *info)
static void actionHandler(int signum, siginfo_t *info, void *)
static DebuggerProgram debugger
void generateStackTrace(quintptr ip)
static void printSentSignalInfo(...)
#define __has_include(x)
#define MAP_FAILED
#define _PATH_DEFPATH
static ssize_t writeToStderr(Args &&... args)
static const char * signalName(int signum) noexcept
#define SIGSTKSZ
#define SA_RESETHAND
static constexpr std::array fatalSignals
static constexpr std::array crashingSignals
#define OUR_SIGNALS(F)