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
|
/*
* signal.c: Register a sigaltstack for objtool, to be able to
* run a signal handler on a separate stack even if
* the main process stack has overflown. Print out
* stack overflow errors when this happens.
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/resource.h>
#include <string.h>
#include <objtool/objtool.h>
#include <objtool/warn.h>
static unsigned long stack_limit;
static bool is_stack_overflow(void *fault_addr)
{
unsigned long fault = (unsigned long)fault_addr;
/* Check if fault is in the guard page just below the limit. */
return fault < stack_limit && fault >= stack_limit - 4096;
}
static void signal_handler(int sig_num, siginfo_t *info, void *context)
{
struct sigaction sa_dfl = {0};
const char *sig_name;
char msg[256];
int msg_len;
switch (sig_num) {
case SIGSEGV: sig_name = "SIGSEGV"; break;
case SIGBUS: sig_name = "SIGBUS"; break;
case SIGILL: sig_name = "SIGILL"; break;
case SIGABRT: sig_name = "SIGABRT"; break;
default: sig_name = "Unknown signal"; break;
}
if (is_stack_overflow(info->si_addr)) {
msg_len = snprintf(msg, sizeof(msg),
"%s: error: %s: objtool stack overflow!\n",
objname, sig_name);
} else {
msg_len = snprintf(msg, sizeof(msg),
"%s: error: %s: objtool crash!\n",
objname, sig_name);
}
msg_len = write(STDERR_FILENO, msg, msg_len);
/* Re-raise the signal to trigger the core dump */
sa_dfl.sa_handler = SIG_DFL;
sigaction(sig_num, &sa_dfl, NULL);
raise(sig_num);
}
static int read_stack_limit(void)
{
unsigned long stack_start, stack_end;
struct rlimit rlim;
char line[256];
int ret = 0;
FILE *fp;
if (getrlimit(RLIMIT_STACK, &rlim)) {
ERROR_GLIBC("getrlimit");
return -1;
}
fp = fopen("/proc/self/maps", "r");
if (!fp) {
ERROR_GLIBC("fopen");
return -1;
}
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, "[stack]")) {
if (sscanf(line, "%lx-%lx", &stack_start, &stack_end) != 2) {
ERROR_GLIBC("sscanf");
ret = -1;
goto done;
}
stack_limit = stack_end - rlim.rlim_cur;
goto done;
}
}
ret = -1;
ERROR("/proc/self/maps: can't find [stack]");
done:
fclose(fp);
return ret;
}
int init_signal_handler(void)
{
int signals[] = {SIGSEGV, SIGBUS, SIGILL, SIGABRT};
struct sigaction sa;
stack_t ss;
if (read_stack_limit())
return -1;
ss.ss_sp = malloc(SIGSTKSZ);
if (!ss.ss_sp) {
ERROR_GLIBC("malloc");
return -1;
}
ss.ss_size = SIGSTKSZ;
ss.ss_flags = 0;
if (sigaltstack(&ss, NULL) == -1) {
ERROR_GLIBC("sigaltstack");
return -1;
}
sa.sa_sigaction = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
for (int i = 0; i < ARRAY_SIZE(signals); i++) {
if (sigaction(signals[i], &sa, NULL) == -1) {
ERROR_GLIBC("sigaction");
return -1;
}
}
return 0;
}
|