x86/debug: Implement __WARN() using UD0
authorPeter Zijlstra <peterz@infradead.org>
Thu, 2 Feb 2017 13:43:51 +0000 (14:43 +0100)
committerIngo Molnar <mingo@kernel.org>
Mon, 27 Mar 2017 08:20:28 +0000 (10:20 +0200)
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>
arch/um/Kconfig.common
arch/x86/include/asm/bug.h
arch/x86/kernel/dumpstack.c
arch/x86/kernel/dumpstack_32.c
arch/x86/kernel/dumpstack_64.c
arch/x86/kernel/traps.c
arch/x86/um/Makefile
arch/x86/um/bug.c [deleted file]

index fd443852103c998fd997c04ee64ebb7c32716785..ed9c5b5ff028947061bd8a86949c58e9456e98a3 100644 (file)
@@ -50,11 +50,6 @@ config GENERIC_CALIBRATE_DELAY
        bool
        default y
 
-config GENERIC_BUG
-       bool
-       default y
-       depends on BUG
-
 config HZ
        int
        default 100
index ba38ebbaced3cd0224caef932f3f59a5cc2cc5cf..4fde330c44b7efa95157679fdd92bab7c50dcb24 100644 (file)
@@ -1,36 +1,78 @@
 #ifndef _ASM_X86_BUG_H
 #define _ASM_X86_BUG_H
 
-#define HAVE_ARCH_BUG
+#include <linux/stringify.h>
 
-#ifdef CONFIG_DEBUG_BUGVERBOSE
+/*
+ * Since some emulators terminate on UD2, we cannot use it for WARN.
+ * Since various instruction decoders disagree on the length of UD1,
+ * we cannot use it either. So use UD0 for WARN.
+ *
+ * (binutils knows about "ud1" but {en,de}codes it as 2 bytes, whereas
+ *  our kernel decoder thinks it takes a ModRM byte, which seems consistent
+ *  with various things like the Intel SDM instruction encoding rules)
+ */
+
+#define ASM_UD0                ".byte 0x0f, 0xff"
+#define ASM_UD1                ".byte 0x0f, 0xb9" /* + ModRM */
+#define ASM_UD2                ".byte 0x0f, 0x0b"
+
+#define INSN_UD0       0xff0f
+#define INSN_UD2       0x0b0f
+
+#define LEN_UD0                2
+
+#ifdef CONFIG_GENERIC_BUG
+#define HAVE_ARCH_BUG
 
 #ifdef CONFIG_X86_32
-# define __BUG_C0      "2:\t.long 1b, %c0\n"
+# define __BUG_REL(val)        ".long " __stringify(val)
 #else
-# define __BUG_C0      "2:\t.long 1b - 2b, %c0 - 2b\n"
+# define __BUG_REL(val)        ".long " __stringify(val) " - 2b"
 #endif
 
-#define BUG()                                                  \
-do {                                                           \
-       asm volatile("1:\tud2\n"                                \
-                    ".pushsection __bug_table,\"a\"\n"         \
-                    __BUG_C0                                   \
-                    "\t.word %c1, 0\n"                         \
-                    "\t.org 2b+%c2\n"                          \
-                    ".popsection"                              \
-                    : : "i" (__FILE__), "i" (__LINE__),        \
-                    "i" (sizeof(struct bug_entry)));           \
-       unreachable();                                          \
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+
+#define _BUG_FLAGS(ins, flags)                                         \
+do {                                                                   \
+       asm volatile("1:\t" ins "\n"                                    \
+                    ".pushsection __bug_table,\"a\"\n"                 \
+                    "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n"   \
+                    "\t"  __BUG_REL(%c0) "\t# bug_entry::file\n"       \
+                    "\t.word %c1"        "\t# bug_entry::line\n"       \
+                    "\t.word %c2"        "\t# bug_entry::flags\n"      \
+                    "\t.org 2b+%c3\n"                                  \
+                    ".popsection"                                      \
+                    : : "i" (__FILE__), "i" (__LINE__),                \
+                        "i" (flags),                                   \
+                        "i" (sizeof(struct bug_entry)));               \
 } while (0)
 
-#else
+#else /* !CONFIG_DEBUG_BUGVERBOSE */
+
+#define _BUG_FLAGS(ins, flags)                                         \
+do {                                                                   \
+       asm volatile("1:\t" ins "\n"                                    \
+                    ".pushsection __bug_table,\"a\"\n"                 \
+                    "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n"   \
+                    "\t.word %c0"        "\t# bug_entry::flags\n"      \
+                    "\t.org 2b+%c1\n"                                  \
+                    ".popsection"                                      \
+                    : : "i" (flags),                                   \
+                        "i" (sizeof(struct bug_entry)));               \
+} while (0)
+
+#endif /* CONFIG_DEBUG_BUGVERBOSE */
+
 #define BUG()                                                  \
 do {                                                           \
-       asm volatile("ud2");                                    \
+       _BUG_FLAGS(ASM_UD2, 0);                                 \
        unreachable();                                          \
 } while (0)
-#endif
+
+#define __WARN_TAINT(taint)    _BUG_FLAGS(ASM_UD0, BUGFLAG_TAINT(taint))
+
+#endif /* CONFIG_GENERIC_BUG */
 
 #include <asm-generic/bug.h>
 
index 09d4ac0d2661fdda13f7ae3acd7412bd6b988d7d..924f45ea43820f4805684c998cea9e535b7b3a19 100644 (file)
@@ -289,9 +289,6 @@ void die(const char *str, struct pt_regs *regs, long err)
        unsigned long flags = oops_begin();
        int sig = SIGSEGV;
 
-       if (!user_mode(regs))
-               report_bug(regs->ip, regs);
-
        if (__die(str, regs, err))
                sig = 0;
        oops_end(flags, regs, sig);
index b0b3a3df7c2080d3d526908441fa93b449b6708f..e5f0b40e66d238b931d14d78ea579abd195a3350 100644 (file)
@@ -162,15 +162,3 @@ void show_regs(struct pt_regs *regs)
        }
        pr_cont("\n");
 }
-
-int is_valid_bugaddr(unsigned long ip)
-{
-       unsigned short ud2;
-
-       if (ip < PAGE_OFFSET)
-               return 0;
-       if (probe_kernel_address((unsigned short *)ip, ud2))
-               return 0;
-
-       return ud2 == 0x0b0f;
-}
index a8b117e93b4620181b3b471049d1ce4925f33ec3..3e1471d5748723069d70577fd9061a1cb26770ba 100644 (file)
@@ -178,13 +178,3 @@ void show_regs(struct pt_regs *regs)
        }
        pr_cont("\n");
 }
-
-int is_valid_bugaddr(unsigned long ip)
-{
-       unsigned short ud2;
-
-       if (__copy_from_user(&ud2, (const void __user *) ip, sizeof(ud2)))
-               return 0;
-
-       return ud2 == 0x0b0f;
-}
index 948443e115c147c28a6445eca725fad698d1a56f..3c0751b120de77958a49b3eba8e43375d377b10a 100644 (file)
@@ -169,6 +169,37 @@ void ist_end_non_atomic(void)
        preempt_disable();
 }
 
+int is_valid_bugaddr(unsigned long addr)
+{
+       unsigned short ud;
+
+       if (addr < TASK_SIZE_MAX)
+               return 0;
+
+       if (probe_kernel_address((unsigned short *)addr, ud))
+               return 0;
+
+       return ud == INSN_UD0 || ud == INSN_UD2;
+}
+
+static int fixup_bug(struct pt_regs *regs, int trapnr)
+{
+       if (trapnr != X86_TRAP_UD)
+               return 0;
+
+       switch (report_bug(regs->ip, regs)) {
+       case BUG_TRAP_TYPE_NONE:
+       case BUG_TRAP_TYPE_BUG:
+               break;
+
+       case BUG_TRAP_TYPE_WARN:
+               regs->ip += LEN_UD0;
+               return 1;
+       }
+
+       return 0;
+}
+
 static nokprobe_inline int
 do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
                  struct pt_regs *regs, long error_code)
@@ -187,12 +218,15 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
        }
 
        if (!user_mode(regs)) {
-               if (!fixup_exception(regs, trapnr)) {
-                       tsk->thread.error_code = error_code;
-                       tsk->thread.trap_nr = trapnr;
-                       die(str, regs, error_code);
-               }
-               return 0;
+               if (fixup_exception(regs, trapnr))
+                       return 0;
+
+               if (fixup_bug(regs, trapnr))
+                       return 0;
+
+               tsk->thread.error_code = error_code;
+               tsk->thread.trap_nr = trapnr;
+               die(str, regs, error_code);
        }
 
        return -1;
index e7e7055a86589dea6d0f9ca043365db4ab8b1b9c..76f17f05446f36b5f1114c2a405fb46f959397ea 100644 (file)
@@ -8,7 +8,7 @@ else
        BITS := 64
 endif
 
-obj-y = bug.o bugs_$(BITS).o delay.o fault.o ldt.o \
+obj-y = bugs_$(BITS).o delay.o fault.o ldt.o \
        ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \
        stub_$(BITS).o stub_segv.o \
        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 (file)
index e8034e3..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com)
- * Licensed under the GPL V2
- */
-
-#include <linux/uaccess.h>
-
-/*
- * Mostly copied from i386/x86_86 - eliminated the eip < PAGE_OFFSET because
- * that's not relevant in skas mode.
- */
-
-int is_valid_bugaddr(unsigned long eip)
-{
-       unsigned short ud2;
-
-       if (probe_kernel_address((unsigned short __user *)eip, ud2))
-               return 0;
-
-       return ud2 == 0x0b0f;
-}