arm: ensure dump_instr() checks addr_limit
Subsystems (2)
PATCH 1/1 arm: ensure dump_instr() checks addr_limit
When CONFIG_DEBUG_USER is enabled, it's possible for a user to deliberately trigger dump_instr() with a chosen kernel address.
Let's avoid problems resulting from this by using get_user() rather than __get_user(), ensuring that we don't erroneously access kernel memory.
So that we can use the same code to dump user instructions and kernel instructions, the common dumping code is factored out to __dump_instr(), with the fs manipulated appropriately in dump_instr() around calls to this.
--- arch/arm/kernel/traps.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-)
arch/arm/kernel/traps.c +18 -10
@@ -154,30 +154,26 @@ static void dump_mem(const char *lvl, const char *str, unsigned long bottom,set_fs (fs); } -static void dump_instr (const char *lvl,struct pt_regs *regs) +static void __dump_instr (const char *lvl,struct pt_regs *regs) {unsigned long addr =instruction_pointer (regs);const int thumb =thumb_mode (regs);const int width = thumb ?4 :8 ; -mm_segment_t fs;char str[sizeof ("00000000 " ) *5 +2 +1 ], *p = str;int i;/* -* We need to switch to kernel mode so that we can use __get_user -* to safely read from kernel space. Note that we now dump the -* code first, just in case the backtrace kills us. +* Note that we now dump the code first, just in case the backtrace +* kills us. */ - fs =get_fs (); -set_fs (KERNEL_DS );for (i =-4 ; i <1 + !!thumb; i++) {unsigned int val, bad;if (thumb) - bad =__get_user (val, &((u16 *)addr)[i]); + bad =get_user (val, &((u16 *)addr)[i]);else - bad =__get_user (val, &((u32 *)addr)[i]); + bad =get_user (val, &((u32 *)addr)[i]);if (!bad) p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ",
@@ -188,8 +184,20 @@ static void dump_instr(const char *lvl, struct pt_regs *regs) } }printk ("%sCode: %s\n" , lvl, str); +} -set_fs (fs); +static void dump_instr (const char *lvl,struct pt_regs *regs) +{ +mm_segment_t fs; + +if (!user_mode (regs)) { + fs =get_fs (); +set_fs (KERNEL_DS ); +__dump_instr (lvl, regs); +set_fs (fs); + }else { +__dump_instr (lvl, regs); + } }#ifdef CONFIG_ARM_UNWIND
Signature · 1 line
2.11.0
Greg KH
maintainerPATCH 1/1 arm: ensure dump_instr() checks addr_limit
Quoted 1 line · Mark Rutland
Signed-off-by Mark Rutland <[email protected]>
Huh? What's that doing up here?
Quoted 6 lines
When
CONFIG_DEBUG_USERis enabled, it's possible for a user to deliberately triggerdump_instr()with a chosen kernel address.Let's avoid problems resulting from this by using
get_user()rather than__get_user(), ensuring that we don't erroneously access kernel memory.
Quoted 8 lines
So that we can use the same code to dump user instructions and kernel instructions, the common dumping code is factored out to
__dump_instr(), with the fs manipulated appropriately indump_instr()around calls to this.Signed-off-by Mark Rutland <[email protected]>Cc Russell King <[email protected]>
It's right here...
confused.
greg k-h
PATCH 1/1 arm: ensure dump_instr() checks addr_limit
Quoted 4 lines · Greg KH
Quoted 1 line · Mark Rutland
Signed-off-by Mark Rutland <[email protected]>Huh? What's that doing up here?
[...]
Quoted 5 lines
Quoted 3 lines
Signed-off-by Mark Rutland <[email protected]>Cc Russell King <[email protected]>Cc stable at vger.kernel.orgIt's right here...
Sorry; I'd generated this commit message from another. I must've accidentally passed -s to git commit before copying the text I wanted.
I will be more careful in future, and I'll remove the first instance if/when submitting this to Russell's patch system.
Thanks,
Mark.
Greg KH
maintainerPATCH 1/1 arm: ensure dump_instr() checks addr_limit
Quoted 12 lines · Mark Rutland
Quoted 4 lines · Greg KH
Quoted 1 line · Mark Rutland
Signed-off-by Mark Rutland <[email protected]>Huh? What's that doing up here?
[...]
Quoted 4 lines
Quoted 3 lines
Signed-off-by Mark Rutland <[email protected]>Cc Russell King <[email protected]>
Quoted 7 lines
Quoted 1 line
It's right here...
Sorry; I'd generated this commit message from another. I must've accidentally passed -s to git commit before copying the text I wanted.
I will be more careful in future, and I'll remove the first instance if/when submitting this to Russell's patch system.
Russell's patch system? That's still in use and required? Ugh, that's crazy...
Anyway, thanks for fixing it up.
greg k-h
Russell King - ARM Linux
maintainerPATCH 1/1 arm: ensure dump_instr() checks addr_limit
Quoted 15 lines · Greg KH
Quoted 14 lines · Mark Rutland
Quoted 4 lines · Greg KH
Quoted 1 line · Mark Rutland
Signed-off-by Mark Rutland <[email protected]>Huh? What's that doing up here?
[...]
Quoted 5 lines
Quoted 3 lines
Signed-off-by Mark Rutland <[email protected]>Cc Russell King <[email protected]>Cc stable at vger.kernel.orgIt's right here...
Quoted 8 lines
Quoted 5 lines
Sorry; I'd generated this commit message from another. I must've accidentally passed -s to git commit before copying the text I wanted.
I will be more careful in future, and I'll remove the first instance if/when submitting this to Russell's patch system.
Russell's patch system? That's still in use and required? Ugh, that's crazy...
Better than me missing patches because they've been buried under tonnes of email. It also does a fair number of checks, and there's been some discussion today about adding to the checks it does to catch stupidity in what the binutils assembler now accepts (to stop patches that appear to be tested but are broken from getting in, as what happened last night.)
Signature · 3 lines
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/ FTTC broadband for 0.8mile line in suburbia: sync at 8.8Mbps down 630kbps up According to speedtest.net: 8.21Mbps down 510kbps up
Russell King - ARM Linux
maintainerPATCH 1/1 arm: ensure dump_instr() checks addr_limit
In reply to Mark RutlandQuoted 4 lines · Mark Rutland
Signed-off-by Mark Rutland <[email protected]>When
CONFIG_DEBUG_USERis enabled, it's possible for a user to deliberately triggerdump_instr()with a chosen kernel address.
Hi Mark,
Please show how userspace could trigger that, as I don't think it's possible. Firstly, you need to set PC to a kernel address, which will trigger an abort.
When that happens, we'll get a section permission fault, and head to do_sect_fault(). This will call do_bad_area(), which will detect that it's a userspace fault (because of the CPSR value).
This calls show_pte() and show_regs(). show_pte() shows the page table values only. show_regs() shows the register values, and then dumps the kernel stack via show_stack(). Nothing in this path calls dump_instr().
The places where dump_instr() is called from are:
die() do_undefinstr() bad_syscall() arm_syscall() baddataabort() (only for late v4 faulting architectures)
The last four all need the CPU to have read and attempted to execute the instruction, so the permission checks must have passed. Userspace can't achieve that by setting the PC to a kernel address.
die() is called when we enter an exception in an invalid mode, or we have a kernel mode fault (CPSR in kernel mode) that we can't handle, we have fault handling disabled (never the case when running user code), or we have no mm (kernel thread).
So, I don't see how the kernel could be provoked to dump instructions from kernel space by the user.
Please show a working example.
Thanks.
Signature · 3 lines
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/ FTTC broadband for 0.8mile line in suburbia: sync at 8.8Mbps down 630kbps up According to speedtest.net: 8.21Mbps down 510kbps up