FatalSignalHandler: use mmap() to create the alternate stack
So we can mark the bottom page as inaccessible, thus be able to catch a stack overflow in the handler itself. Our code shouldn't cause overflows, but it's possible that a chained handler does more work than expected. Pick-to: 6.3 Change-Id: I5ff8e16fcdcb4ffd9ab6fffd16eb83a294ab7958 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
parent
e390ff050a
commit
d5f4f91c3c
@ -82,12 +82,16 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
# if !defined(Q_OS_INTEGRITY)
|
# if !defined(Q_OS_INTEGRITY)
|
||||||
# include <sys/resource.h>
|
# include <sys/resource.h>
|
||||||
# endif
|
# endif
|
||||||
|
# ifndef SIGSTKSZ
|
||||||
|
# define SIGSTKSZ 0 /* we have code to set the minimum */
|
||||||
|
# endif
|
||||||
# ifndef SA_RESETHAND
|
# ifndef SA_RESETHAND
|
||||||
# define SA_RESETHAND 0
|
# define SA_RESETHAND 0
|
||||||
# endif
|
# endif
|
||||||
@ -1865,7 +1869,7 @@ public:
|
|||||||
oldActions().fill(act);
|
oldActions().fill(act);
|
||||||
|
|
||||||
// Remove the handler after it is invoked.
|
// Remove the handler after it is invoked.
|
||||||
act.sa_flags = SA_RESETHAND;
|
act.sa_flags = SA_RESETHAND | setupAlternateStack();
|
||||||
|
|
||||||
# ifdef SA_SIGINFO
|
# ifdef SA_SIGINFO
|
||||||
act.sa_flags |= SA_SIGINFO;
|
act.sa_flags |= SA_SIGINFO;
|
||||||
@ -1874,26 +1878,6 @@ public:
|
|||||||
act.sa_handler = FatalSignalHandler::regularHandler;
|
act.sa_handler = FatalSignalHandler::regularHandler;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
// tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as
|
|
||||||
// unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h)
|
|
||||||
# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
|
|
||||||
// Let the signal handlers use an alternate stack
|
|
||||||
// This is necessary if SIGSEGV is to catch a stack overflow
|
|
||||||
# if defined(Q_CC_GNU) && defined(Q_OF_ELF)
|
|
||||||
// Put the alternate stack in the .lbss (large BSS) section so that it doesn't
|
|
||||||
// interfere with normal .bss symbols
|
|
||||||
__attribute__((section(".lbss.altstack"), aligned(4096)))
|
|
||||||
# endif
|
|
||||||
static QVarLengthArray<char, 32 * 1024> alternateStack;
|
|
||||||
alternateStack.resize(qMax(SIGSTKSZ, alternateStack.size()));
|
|
||||||
stack_t stack;
|
|
||||||
stack.ss_flags = 0;
|
|
||||||
stack.ss_size = alternateStack.size();
|
|
||||||
stack.ss_sp = alternateStack.data();
|
|
||||||
sigaltstack(&stack, nullptr);
|
|
||||||
act.sa_flags |= SA_ONSTACK;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
// Block all fatal signals in our signal handler so we don't try to close
|
// Block all fatal signals in our signal handler so we don't try to close
|
||||||
// the testlog twice.
|
// the testlog twice.
|
||||||
sigemptyset(&act.sa_mask);
|
sigemptyset(&act.sa_mask);
|
||||||
@ -1926,6 +1910,8 @@ public:
|
|||||||
if (isOurs(action))
|
if (isOurs(action))
|
||||||
sigaction(fatalSignals[i], &act, nullptr);
|
sigaction(fatalSignals[i], &act, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
freeAlternateStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -1937,6 +1923,64 @@ private:
|
|||||||
return oldActions;
|
return oldActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto alternateStackSize()
|
||||||
|
{
|
||||||
|
struct R { size_t size, pageSize; };
|
||||||
|
static constexpr size_t MinStackSize = 32 * 1024;
|
||||||
|
size_t pageSize = sysconf(_SC_PAGESIZE);
|
||||||
|
size_t size = SIGSTKSZ;
|
||||||
|
if (size < MinStackSize) {
|
||||||
|
size = MinStackSize;
|
||||||
|
} else {
|
||||||
|
// round up to a page
|
||||||
|
size = (size + pageSize - 1) & -pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return R{ size + pageSize, pageSize };
|
||||||
|
}
|
||||||
|
|
||||||
|
int setupAlternateStack()
|
||||||
|
{
|
||||||
|
// tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as
|
||||||
|
// unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h)
|
||||||
|
# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
|
||||||
|
// Let the signal handlers use an alternate stack
|
||||||
|
// This is necessary if SIGSEGV is to catch a stack overflow
|
||||||
|
auto r = alternateStackSize();
|
||||||
|
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
||||||
|
# ifdef MAP_STACK
|
||||||
|
flags |= MAP_STACK;
|
||||||
|
# endif
|
||||||
|
alternateStackBase = mmap(nullptr, r.size, PROT_READ | PROT_WRITE, flags, -1, 0);
|
||||||
|
if (alternateStackBase == MAP_FAILED)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// mark the bottom page inaccessible, to catch a handler stack overflow
|
||||||
|
(void) mprotect(alternateStackBase, r.pageSize, PROT_NONE);
|
||||||
|
|
||||||
|
stack_t stack;
|
||||||
|
stack.ss_flags = 0;
|
||||||
|
stack.ss_size = r.size - r.pageSize;
|
||||||
|
stack.ss_sp = static_cast<char *>(alternateStackBase) + r.pageSize;
|
||||||
|
sigaltstack(&stack, nullptr);
|
||||||
|
return SA_ONSTACK;
|
||||||
|
# else
|
||||||
|
return 0;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeAlternateStack()
|
||||||
|
{
|
||||||
|
# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
|
||||||
|
if (alternateStackBase != MAP_FAILED) {
|
||||||
|
stack_t stack = {};
|
||||||
|
stack.ss_flags = SS_DISABLE;
|
||||||
|
sigaltstack(&stack, nullptr);
|
||||||
|
munmap(alternateStackBase, alternateStackSize().size);
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
static void actionHandler(int signum, siginfo_t * /* info */, void * /* ucontext */)
|
static void actionHandler(int signum, siginfo_t * /* info */, void * /* ucontext */)
|
||||||
{
|
{
|
||||||
const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
|
const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
|
||||||
@ -1982,6 +2026,8 @@ private:
|
|||||||
{
|
{
|
||||||
actionHandler(signum, nullptr, nullptr);
|
actionHandler(signum, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *alternateStackBase = MAP_FAILED;
|
||||||
static bool pauseOnCrash;
|
static bool pauseOnCrash;
|
||||||
};
|
};
|
||||||
bool FatalSignalHandler::pauseOnCrash = false;
|
bool FatalSignalHandler::pauseOnCrash = false;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user