From: Borislav Petkov Date: Tue, 17 Apr 2018 16:11:19 +0000 (+0200) Subject: x86/dumpstack: Improve opcodes dumping in the code section X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=9e4a90fd34445df64a13d136676a31a4dd22aea3;p=openwrt%2Fstaging%2Fblogic.git x86/dumpstack: Improve opcodes dumping in the code section The code used to iterate byte-by-byte over the bytes around RIP and that is expensive: disabling pagefaults around it, copy_from_user, etc... Make it read the whole buffer of OPCODE_BUFSIZE size in one go. Use a statically allocated 64 bytes buffer so that concurrent show_opcodes() do not interleave in the output even though in the majority of the cases it's serialized via die_lock. Except the #PF path which doesn't... Also, do the PAGE_OFFSET check outside of the function because latter will be reused in other context. Signed-off-by: Borislav Petkov Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Cc: Josh Poimboeuf Cc: Linus Torvalds Cc: Andy Lutomirski Link: https://lkml.kernel.org/r/20180417161124.5294-5-bp@alien8.de --- diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index eb9d6c00a52f..1d6698b54527 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c @@ -72,29 +72,24 @@ static void printk_stack_address(unsigned long address, int reliable, static void show_opcodes(u8 *rip) { - unsigned int code_prologue = OPCODE_BUFSIZE * 43 / 64; - unsigned int code_len = OPCODE_BUFSIZE; - unsigned char c; + unsigned int code_prologue = OPCODE_BUFSIZE * 2 / 3; + u8 opcodes[OPCODE_BUFSIZE]; u8 *ip; int i; printk(KERN_DEFAULT "Code: "); ip = (u8 *)rip - code_prologue; - if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { - /* try starting at IP */ - ip = (u8 *)rip; - code_len = code_len - code_prologue + 1; + if (probe_kernel_read(opcodes, ip, OPCODE_BUFSIZE)) { + pr_cont("Bad RIP value.\n"); + return; } - for (i = 0; i < code_len; i++, ip++) { - if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { - pr_cont(" Bad RIP value."); - break; - } - if (ip == (u8 *)rip) - pr_cont("<%02x> ", c); + + for (i = 0; i < OPCODE_BUFSIZE; i++, ip++) { + if (ip == rip) + pr_cont("<%02x> ", opcodes[i]); else - pr_cont("%02x ", c); + pr_cont("%02x ", opcodes[i]); } pr_cont("\n"); } @@ -402,6 +397,10 @@ void show_regs(struct pt_regs *regs) */ if (!user_mode(regs)) { show_trace_log_lvl(current, regs, NULL, KERN_DEFAULT); - show_opcodes((u8 *)regs->ip); + + if (regs->ip < PAGE_OFFSET) + printk(KERN_DEFAULT "Code: Bad RIP value.\n"); + else + show_opcodes((u8 *)regs->ip); } }