diff options
author | Peter Zijlstra <peterz@infradead.org> | 2017-02-02 08:43:51 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2017-03-27 04:20:28 -0400 |
commit | 9a93848fe787a53aec404e4e00d8f7f9bbdaebb4 (patch) | |
tree | 4e3b4f0b97838278a72f47a4b4ed2825610e1969 | |
parent | 1fa9d67a2f07893fc7e4a880867a6cbb81dda547 (diff) |
x86/debug: Implement __WARN() using UD0
By using "UD0" for WARN()s we remove the function call and its possible
__FILE__ and __LINE__ immediate arguments from the instruction stream.
Total image size will not change much, what we win in the instruction
stream we'll lose because of the __bug_table entries. Still, saves on
I$ footprint and the total image size does go down a bit.
text data filename
10702123 4530992 defconfig-build/vmlinux.orig
10682460 4530992 defconfig-build/vmlinux.patched
(UML didn't seem to use GENERIC_BUG at all, so remove it)
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Arjan van de Ven <arjan@linux.intel.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Richard Weinberger <richard.weinberger@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | arch/um/Kconfig.common | 5 | ||||
-rw-r--r-- | arch/x86/include/asm/bug.h | 78 | ||||
-rw-r--r-- | arch/x86/kernel/dumpstack.c | 3 | ||||
-rw-r--r-- | arch/x86/kernel/dumpstack_32.c | 12 | ||||
-rw-r--r-- | arch/x86/kernel/dumpstack_64.c | 10 | ||||
-rw-r--r-- | arch/x86/kernel/traps.c | 46 | ||||
-rw-r--r-- | arch/x86/um/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/um/bug.c | 21 |
8 files changed, 101 insertions, 76 deletions
diff --git a/arch/um/Kconfig.common b/arch/um/Kconfig.common index fd443852103c..ed9c5b5ff028 100644 --- a/arch/um/Kconfig.common +++ b/arch/um/Kconfig.common | |||
@@ -50,11 +50,6 @@ config GENERIC_CALIBRATE_DELAY | |||
50 | bool | 50 | bool |
51 | default y | 51 | default y |
52 | 52 | ||
53 | config GENERIC_BUG | ||
54 | bool | ||
55 | default y | ||
56 | depends on BUG | ||
57 | |||
58 | config HZ | 53 | config HZ |
59 | int | 54 | int |
60 | default 100 | 55 | default 100 |
diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h index ba38ebbaced3..4fde330c44b7 100644 --- a/arch/x86/include/asm/bug.h +++ b/arch/x86/include/asm/bug.h | |||
@@ -1,36 +1,78 @@ | |||
1 | #ifndef _ASM_X86_BUG_H | 1 | #ifndef _ASM_X86_BUG_H |
2 | #define _ASM_X86_BUG_H | 2 | #define _ASM_X86_BUG_H |
3 | 3 | ||
4 | #define HAVE_ARCH_BUG | 4 | #include <linux/stringify.h> |
5 | 5 | ||
6 | #ifdef CONFIG_DEBUG_BUGVERBOSE | 6 | /* |
7 | * Since some emulators terminate on UD2, we cannot use it for WARN. | ||
8 | * Since various instruction decoders disagree on the length of UD1, | ||
9 | * we cannot use it either. So use UD0 for WARN. | ||
10 | * | ||
11 | * (binutils knows about "ud1" but {en,de}codes it as 2 bytes, whereas | ||
12 | * our kernel decoder thinks it takes a ModRM byte, which seems consistent | ||
13 | * with various things like the Intel SDM instruction encoding rules) | ||
14 | */ | ||
15 | |||
16 | #define ASM_UD0 ".byte 0x0f, 0xff" | ||
17 | #define ASM_UD1 ".byte 0x0f, 0xb9" /* + ModRM */ | ||
18 | #define ASM_UD2 ".byte 0x0f, 0x0b" | ||
19 | |||
20 | #define INSN_UD0 0xff0f | ||
21 | #define INSN_UD2 0x0b0f | ||
22 | |||
23 | #define LEN_UD0 2 | ||
24 | |||
25 | #ifdef CONFIG_GENERIC_BUG | ||
26 | #define HAVE_ARCH_BUG | ||
7 | 27 | ||
8 | #ifdef CONFIG_X86_32 | 28 | #ifdef CONFIG_X86_32 |
9 | # define __BUG_C0 "2:\t.long 1b, %c0\n" | 29 | # define __BUG_REL(val) ".long " __stringify(val) |
10 | #else | 30 | #else |
11 | # define __BUG_C0 "2:\t.long 1b - 2b, %c0 - 2b\n" | 31 | # define __BUG_REL(val) ".long " __stringify(val) " - 2b" |
12 | #endif | 32 | #endif |
13 | 33 | ||
14 | #define BUG() \ | 34 | #ifdef CONFIG_DEBUG_BUGVERBOSE |
15 | do { \ | 35 | |
16 | asm volatile("1:\tud2\n" \ | 36 | #define _BUG_FLAGS(ins, flags) \ |
17 | ".pushsection __bug_table,\"a\"\n" \ | 37 | do { \ |
18 | __BUG_C0 \ | 38 | asm volatile("1:\t" ins "\n" \ |
19 | "\t.word %c1, 0\n" \ | 39 | ".pushsection __bug_table,\"a\"\n" \ |
20 | "\t.org 2b+%c2\n" \ | 40 | "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n" \ |
21 | ".popsection" \ | 41 | "\t" __BUG_REL(%c0) "\t# bug_entry::file\n" \ |
22 | : : "i" (__FILE__), "i" (__LINE__), \ | 42 | "\t.word %c1" "\t# bug_entry::line\n" \ |
23 | "i" (sizeof(struct bug_entry))); \ | 43 | "\t.word %c2" "\t# bug_entry::flags\n" \ |
24 | unreachable(); \ | 44 | "\t.org 2b+%c3\n" \ |
45 | ".popsection" \ | ||
46 | : : "i" (__FILE__), "i" (__LINE__), \ | ||
47 | "i" (flags), \ | ||
48 | "i" (sizeof(struct bug_entry))); \ | ||
25 | } while (0) | 49 | } while (0) |
26 | 50 | ||
27 | #else | 51 | #else /* !CONFIG_DEBUG_BUGVERBOSE */ |
52 | |||
53 | #define _BUG_FLAGS(ins, flags) \ | ||
54 | do { \ | ||
55 | asm volatile("1:\t" ins "\n" \ | ||
56 | ".pushsection __bug_table,\"a\"\n" \ | ||
57 | "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n" \ | ||
58 | "\t.word %c0" "\t# bug_entry::flags\n" \ | ||
59 | "\t.org 2b+%c1\n" \ | ||
60 | ".popsection" \ | ||
61 | : : "i" (flags), \ | ||
62 | "i" (sizeof(struct bug_entry))); \ | ||
63 | } while (0) | ||
64 | |||
65 | #endif /* CONFIG_DEBUG_BUGVERBOSE */ | ||
66 | |||
28 | #define BUG() \ | 67 | #define BUG() \ |
29 | do { \ | 68 | do { \ |
30 | asm volatile("ud2"); \ | 69 | _BUG_FLAGS(ASM_UD2, 0); \ |
31 | unreachable(); \ | 70 | unreachable(); \ |
32 | } while (0) | 71 | } while (0) |
33 | #endif | 72 | |
73 | #define __WARN_TAINT(taint) _BUG_FLAGS(ASM_UD0, BUGFLAG_TAINT(taint)) | ||
74 | |||
75 | #endif /* CONFIG_GENERIC_BUG */ | ||
34 | 76 | ||
35 | #include <asm-generic/bug.h> | 77 | #include <asm-generic/bug.h> |
36 | 78 | ||
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index 09d4ac0d2661..924f45ea4382 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c | |||
@@ -289,9 +289,6 @@ void die(const char *str, struct pt_regs *regs, long err) | |||
289 | unsigned long flags = oops_begin(); | 289 | unsigned long flags = oops_begin(); |
290 | int sig = SIGSEGV; | 290 | int sig = SIGSEGV; |
291 | 291 | ||
292 | if (!user_mode(regs)) | ||
293 | report_bug(regs->ip, regs); | ||
294 | |||
295 | if (__die(str, regs, err)) | 292 | if (__die(str, regs, err)) |
296 | sig = 0; | 293 | sig = 0; |
297 | oops_end(flags, regs, sig); | 294 | oops_end(flags, regs, sig); |
diff --git a/arch/x86/kernel/dumpstack_32.c b/arch/x86/kernel/dumpstack_32.c index b0b3a3df7c20..e5f0b40e66d2 100644 --- a/arch/x86/kernel/dumpstack_32.c +++ b/arch/x86/kernel/dumpstack_32.c | |||
@@ -162,15 +162,3 @@ void show_regs(struct pt_regs *regs) | |||
162 | } | 162 | } |
163 | pr_cont("\n"); | 163 | pr_cont("\n"); |
164 | } | 164 | } |
165 | |||
166 | int is_valid_bugaddr(unsigned long ip) | ||
167 | { | ||
168 | unsigned short ud2; | ||
169 | |||
170 | if (ip < PAGE_OFFSET) | ||
171 | return 0; | ||
172 | if (probe_kernel_address((unsigned short *)ip, ud2)) | ||
173 | return 0; | ||
174 | |||
175 | return ud2 == 0x0b0f; | ||
176 | } | ||
diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c index a8b117e93b46..3e1471d57487 100644 --- a/arch/x86/kernel/dumpstack_64.c +++ b/arch/x86/kernel/dumpstack_64.c | |||
@@ -178,13 +178,3 @@ void show_regs(struct pt_regs *regs) | |||
178 | } | 178 | } |
179 | pr_cont("\n"); | 179 | pr_cont("\n"); |
180 | } | 180 | } |
181 | |||
182 | int is_valid_bugaddr(unsigned long ip) | ||
183 | { | ||
184 | unsigned short ud2; | ||
185 | |||
186 | if (__copy_from_user(&ud2, (const void __user *) ip, sizeof(ud2))) | ||
187 | return 0; | ||
188 | |||
189 | return ud2 == 0x0b0f; | ||
190 | } | ||
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 948443e115c1..3c0751b120de 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c | |||
@@ -169,6 +169,37 @@ void ist_end_non_atomic(void) | |||
169 | preempt_disable(); | 169 | preempt_disable(); |
170 | } | 170 | } |
171 | 171 | ||
172 | int is_valid_bugaddr(unsigned long addr) | ||
173 | { | ||
174 | unsigned short ud; | ||
175 | |||
176 | if (addr < TASK_SIZE_MAX) | ||
177 | return 0; | ||
178 | |||
179 | if (probe_kernel_address((unsigned short *)addr, ud)) | ||
180 | return 0; | ||
181 | |||
182 | return ud == INSN_UD0 || ud == INSN_UD2; | ||
183 | } | ||
184 | |||
185 | static int fixup_bug(struct pt_regs *regs, int trapnr) | ||
186 | { | ||
187 | if (trapnr != X86_TRAP_UD) | ||
188 | return 0; | ||
189 | |||
190 | switch (report_bug(regs->ip, regs)) { | ||
191 | case BUG_TRAP_TYPE_NONE: | ||
192 | case BUG_TRAP_TYPE_BUG: | ||
193 | break; | ||
194 | |||
195 | case BUG_TRAP_TYPE_WARN: | ||
196 | regs->ip += LEN_UD0; | ||
197 | return 1; | ||
198 | } | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
172 | static nokprobe_inline int | 203 | static nokprobe_inline int |
173 | do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str, | 204 | do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str, |
174 | struct pt_regs *regs, long error_code) | 205 | struct pt_regs *regs, long error_code) |
@@ -187,12 +218,15 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str, | |||
187 | } | 218 | } |
188 | 219 | ||
189 | if (!user_mode(regs)) { | 220 | if (!user_mode(regs)) { |
190 | if (!fixup_exception(regs, trapnr)) { | 221 | if (fixup_exception(regs, trapnr)) |
191 | tsk->thread.error_code = error_code; | 222 | return 0; |
192 | tsk->thread.trap_nr = trapnr; | 223 | |
193 | die(str, regs, error_code); | 224 | if (fixup_bug(regs, trapnr)) |
194 | } | 225 | return 0; |
195 | return 0; | 226 | |
227 | tsk->thread.error_code = error_code; | ||
228 | tsk->thread.trap_nr = trapnr; | ||
229 | die(str, regs, error_code); | ||
196 | } | 230 | } |
197 | 231 | ||
198 | return -1; | 232 | return -1; |
diff --git a/arch/x86/um/Makefile b/arch/x86/um/Makefile index e7e7055a8658..76f17f05446f 100644 --- a/arch/x86/um/Makefile +++ b/arch/x86/um/Makefile | |||
@@ -8,7 +8,7 @@ else | |||
8 | BITS := 64 | 8 | BITS := 64 |
9 | endif | 9 | endif |
10 | 10 | ||
11 | obj-y = bug.o bugs_$(BITS).o delay.o fault.o ldt.o \ | 11 | obj-y = bugs_$(BITS).o delay.o fault.o ldt.o \ |
12 | ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \ | 12 | ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \ |
13 | stub_$(BITS).o stub_segv.o \ | 13 | stub_$(BITS).o stub_segv.o \ |
14 | sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \ | 14 | sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \ |
diff --git a/arch/x86/um/bug.c b/arch/x86/um/bug.c deleted file mode 100644 index e8034e363d83..000000000000 --- a/arch/x86/um/bug.c +++ /dev/null | |||
@@ -1,21 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL V2 | ||
4 | */ | ||
5 | |||
6 | #include <linux/uaccess.h> | ||
7 | |||
8 | /* | ||
9 | * Mostly copied from i386/x86_86 - eliminated the eip < PAGE_OFFSET because | ||
10 | * that's not relevant in skas mode. | ||
11 | */ | ||
12 | |||
13 | int is_valid_bugaddr(unsigned long eip) | ||
14 | { | ||
15 | unsigned short ud2; | ||
16 | |||
17 | if (probe_kernel_address((unsigned short __user *)eip, ud2)) | ||
18 | return 0; | ||
19 | |||
20 | return ud2 == 0x0b0f; | ||
21 | } | ||