Skip to content

Commit 799647d

Browse files
jpoimboeingomolnar
authored andcommitted
objtool: Add more robust signal error handling, detect and warn about stack overflows
When the kernel build fails due to an objtool segfault, the error message is a bit obtuse and confusing: make[5]: *** [scripts/Makefile.build:503: drivers/scsi/qla2xxx/qla2xxx.o] Error 139 ^^^^^^^^^ make[5]: *** Deleting file 'drivers/scsi/qla2xxx/qla2xxx.o' make[4]: *** [scripts/Makefile.build:556: drivers/scsi/qla2xxx] Error 2 make[3]: *** [scripts/Makefile.build:556: drivers/scsi] Error 2 make[2]: *** [scripts/Makefile.build:556: drivers] Error 2 make[1]: *** [/home/jpoimboe/git/linux/Makefile:2013: .] Error 2 make: *** [Makefile:248: __sub-make] Error 2 Add a signal handler to objtool which prints an error message like if the local stack has overflown (for which there's a chance as objtool makes heavy use of recursion): drivers/scsi/qla2xxx/qla2xxx.o: error: SIGSEGV: objtool stack overflow! or: drivers/scsi/qla2xxx/qla2xxx.o: error: SIGSEGV: objtool crash! Also, re-raise the signal so the core dump still gets triggered. [ mingo: Applied a build fix, added more comments and prettified the code. ] Suggested-by: Ingo Molnar <mingo@kernel.org> Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org> Signed-off-by: Ingo Molnar <mingo@kernel.org> Cc: Alexandre Chartre <alexandre.chartre@oracle.com> Cc: David Laight <david.laight.linux@gmail.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Nathan Chancellor <nathan@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Link: https://patch.msgid.link/mi4tihk4dbncn7belrhp6ooudhpw4vdggerktu5333w3gqf3uf@vqlhc3y667mg
1 parent ed3bf86 commit 799647d

4 files changed

Lines changed: 141 additions & 1 deletion

File tree

tools/objtool/Build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ objtool-y += libstring.o
1818
objtool-y += libctype.o
1919
objtool-y += str_error_r.o
2020
objtool-y += librbtree.o
21+
objtool-y += signal.o
2122

2223
$(OUTPUT)libstring.o: ../lib/string.c FORCE
2324
$(call rule_mkdir)

tools/objtool/include/objtool/objtool.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ struct objtool_file {
4141

4242
char *top_level_dir(const char *file);
4343

44+
int init_signal_handler(void);
45+
4446
struct objtool_file *objtool_open_read(const char *_objname);
4547

4648
int objtool_pv_add(struct objtool_file *file, int idx, struct symbol *func);

tools/objtool/objtool.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,13 @@ char *top_level_dir(const char *file)
104104
return str;
105105
}
106106

107-
108107
int main(int argc, const char **argv)
109108
{
110109
static const char *UNUSED = "OBJTOOL_NOT_IMPLEMENTED";
111110

111+
if (init_signal_handler())
112+
return -1;
113+
112114
/* libsubcmd init */
113115
exec_cmd_init("objtool", UNUSED, UNUSED, UNUSED);
114116
pager_init(UNUSED);

tools/objtool/signal.c

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* signal.c: Register a sigaltstack for objtool, to be able to
3+
* run a signal handler on a separate stack even if
4+
* the main process stack has overflown. Print out
5+
* stack overflow errors when this happens.
6+
*/
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include <signal.h>
10+
#include <unistd.h>
11+
#include <sys/resource.h>
12+
#include <string.h>
13+
14+
#include <objtool/objtool.h>
15+
#include <objtool/warn.h>
16+
17+
static unsigned long stack_limit;
18+
19+
static bool is_stack_overflow(void *fault_addr)
20+
{
21+
unsigned long fault = (unsigned long)fault_addr;
22+
23+
/* Check if fault is in the guard page just below the limit. */
24+
return fault < stack_limit && fault >= stack_limit - 4096;
25+
}
26+
27+
static void signal_handler(int sig_num, siginfo_t *info, void *context)
28+
{
29+
struct sigaction sa_dfl = {0};
30+
const char *sig_name;
31+
char msg[256];
32+
int msg_len;
33+
34+
switch (sig_num) {
35+
case SIGSEGV: sig_name = "SIGSEGV"; break;
36+
case SIGBUS: sig_name = "SIGBUS"; break;
37+
case SIGILL: sig_name = "SIGILL"; break;
38+
case SIGABRT: sig_name = "SIGABRT"; break;
39+
default: sig_name = "Unknown signal"; break;
40+
}
41+
42+
if (is_stack_overflow(info->si_addr)) {
43+
msg_len = snprintf(msg, sizeof(msg),
44+
"%s: error: %s: objtool stack overflow!\n",
45+
objname, sig_name);
46+
} else {
47+
msg_len = snprintf(msg, sizeof(msg),
48+
"%s: error: %s: objtool crash!\n",
49+
objname, sig_name);
50+
}
51+
52+
msg_len = write(STDERR_FILENO, msg, msg_len);
53+
54+
/* Re-raise the signal to trigger the core dump */
55+
sa_dfl.sa_handler = SIG_DFL;
56+
sigaction(sig_num, &sa_dfl, NULL);
57+
raise(sig_num);
58+
}
59+
60+
static int read_stack_limit(void)
61+
{
62+
unsigned long stack_start, stack_end;
63+
struct rlimit rlim;
64+
char line[256];
65+
int ret = 0;
66+
FILE *fp;
67+
68+
if (getrlimit(RLIMIT_STACK, &rlim)) {
69+
ERROR_GLIBC("getrlimit");
70+
return -1;
71+
}
72+
73+
fp = fopen("/proc/self/maps", "r");
74+
if (!fp) {
75+
ERROR_GLIBC("fopen");
76+
return -1;
77+
}
78+
79+
while (fgets(line, sizeof(line), fp)) {
80+
if (strstr(line, "[stack]")) {
81+
if (sscanf(line, "%lx-%lx", &stack_start, &stack_end) != 2) {
82+
ERROR_GLIBC("sscanf");
83+
ret = -1;
84+
goto done;
85+
}
86+
stack_limit = stack_end - rlim.rlim_cur;
87+
goto done;
88+
}
89+
}
90+
91+
ret = -1;
92+
ERROR("/proc/self/maps: can't find [stack]");
93+
94+
done:
95+
fclose(fp);
96+
97+
return ret;
98+
}
99+
100+
int init_signal_handler(void)
101+
{
102+
int signals[] = {SIGSEGV, SIGBUS, SIGILL, SIGABRT};
103+
struct sigaction sa;
104+
stack_t ss;
105+
106+
if (read_stack_limit())
107+
return -1;
108+
109+
ss.ss_sp = malloc(SIGSTKSZ);
110+
if (!ss.ss_sp) {
111+
ERROR_GLIBC("malloc");
112+
return -1;
113+
}
114+
ss.ss_size = SIGSTKSZ;
115+
ss.ss_flags = 0;
116+
117+
if (sigaltstack(&ss, NULL) == -1) {
118+
ERROR_GLIBC("sigaltstack");
119+
return -1;
120+
}
121+
122+
sa.sa_sigaction = signal_handler;
123+
sigemptyset(&sa.sa_mask);
124+
125+
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
126+
127+
for (int i = 0; i < ARRAY_SIZE(signals); i++) {
128+
if (sigaction(signals[i], &sa, NULL) == -1) {
129+
ERROR_GLIBC("sigaction");
130+
return -1;
131+
}
132+
}
133+
134+
return 0;
135+
}

0 commit comments

Comments
 (0)