ARM/ARM64: don't enter kgdb when userspace executes a kgdb break instruction.
Subsystems (2)
PATCH 1/1 ARM/ARM64: don't enter kgdb when userspace executes a kgdb break instruction.
The kgdb breakpoint hooks (kgdb_brk_fn and kgdb_compiled_brk_fn) should only be entered when a kgdb break instruction is executed from the kernel. Otherwise, if kgdb is enabled, a userspace program can cause the kernel to drop into the debugger by executing either KGDB_BREAKINST or KGDB_COMPILED_BREAK on ARM, or brk #KGDB_{DYN,COMPILED}_DGB_BRK_IMM on ARM64.
---
The following program reproduces the fixed problem on ARM:
.globl _start_start:
udf #65006 @ KGDB_BREAKINST
And on ARM64:
.globl _start_start:
brk #0x400 @ KGDB_DYN_DGB_BRK_IMM
arch/arm/kernel/kgdb.c | 4 ++++ arch/arm64/include/asm/debug-monitors.h | 4 +++- arch/arm64/kernel/debug-monitors.c | 3 ++- arch/arm64/kernel/kgdb.c | 4 ++++ 4 files changed, 13 insertions(+), 2 deletions(-)
arch/arm/kernel/kgdb.c +4 -0
@@ -160,12 +160,16 @@ static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr)static struct undef_hook kgdb_brkpt_hook = { .instr_mask =0xffffffff , .instr_val =KGDB_BREAKINST , + .cpsr_mask =MODE_MASK , + .cpsr_val =SVC_MODE , .fn = kgdb_brk_fn };static struct undef_hook kgdb_compiled_brkpt_hook = { .instr_mask =0xffffffff , .instr_val =KGDB_COMPILED_BREAK , + .cpsr_mask =MODE_MASK , + .cpsr_val =SVC_MODE , .fn = kgdb_compiled_brk_fn };
arch/arm64/include/asm/debug-monitors.h +3 -1
@@ -105,8 +105,10 @@ void unregister_step_hook(struct step_hook *hook);struct break_hook {struct list_head node; -u32 esr_val;u32 esr_mask; +u32 esr_val; +u64 pstate_mask; +u64 pstate_val;int (*fn)(struct pt_regs *regs,unsigned int esr); };
arch/arm64/kernel/debug-monitors.c +2 -1
@@ -303,7 +303,8 @@ static int call_break_hook(struct pt_regs *regs, unsigned int esr)read_lock (&break_hook_lock);list_for_each_entry (hook, &break_hook, node) -if ((esr & hook->esr_mask) == hook->esr_val) +if ((esr & hook->esr_mask) == hook->esr_val && + (regs->pstate & hook->pstate_mask) == hook->pstate_val) fn = hook->fn;read_unlock (&break_hook_lock);
arch/arm64/kernel/kgdb.c +4 -0
@@ -236,12 +236,16 @@ static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned int esr)static struct break_hook kgdb_brkpt_hook = { .esr_mask =0xffffffff , .esr_val =DBG_ESR_VAL_BRK (KGDB_DYN_DGB_BRK_IMM ), + .pstate_mask =PSR_MODE_MASK , + .pstate_val = PSR_MODE_EL1h, .fn = kgdb_brk_fn };static struct break_hook kgdb_compiled_brkpt_hook = { .esr_mask =0xffffffff , .esr_val =DBG_ESR_VAL_BRK (KDBG_COMPILED_DBG_BRK_IMM ), + .pstate_mask =PSR_MODE_MASK , + .pstate_val = PSR_MODE_EL1h, .fn = kgdb_compiled_brk_fn };
Signature · 1 line
2.0.3
PATCH 1/1 ARM/ARM64: don't enter kgdb when userspace executes a kgdb break instruction.
Quoted 99 lines · Omar Sandoval
The kgdb breakpoint hooks (
kgdb_brk_fnandkgdb_compiled_brk_fn) should only be entered when a kgdb break instruction is executed from the kernel. Otherwise, if kgdb is enabled, a userspace program can cause the kernel to drop into the debugger by executing eitherKGDB_BREAKINSTorKGDB_COMPILED_BREAKon ARM, or brk #KGDB_{DYN,COMPILED}_DGB_BRK_IMM on ARM64.Signed-off-by Omar Sandoval <[email protected]>---
The following program reproduces the fixed problem on ARM:
.globl_start_start:udf #65006 @ KGDB_BREAKINSTAnd on ARM64:
.globl_start_start:brk #0x400 @ KGDB_DYN_DGB_BRK_IMMarch/arm/kernel/kgdb.c | 4 ++++ arch/arm64/include/asm/debug-monitors.h | 4 +++- arch/arm64/kernel/debug-monitors.c | 3 ++- arch/arm64/kernel/kgdb.c | 4 ++++ 4 files changed, 13 insertions(+), 2 deletions(-)arch/arm/kernel/kgdb.c +4
-0@@ -160,12 +160,16 @@ static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr)static struct undef_hook kgdb_brkpt_hook = { .instr_mask =0xffffffff , .instr_val =KGDB_BREAKINST , + .cpsr_mask =MODE_MASK , + .cpsr_val =SVC_MODE , .fn = kgdb_brk_fn };static struct undef_hook kgdb_compiled_brkpt_hook = { .instr_mask =0xffffffff , .instr_val =KGDB_COMPILED_BREAK , + .cpsr_mask =MODE_MASK , + .cpsr_val =SVC_MODE , .fn = kgdb_compiled_brk_fn };arch/arm64/include/asm/debug-monitors.h +3
-1@@ -105,8 +105,10 @@ void unregister_step_hook(struct step_hook *hook);struct break_hook {struct list_head node; -u32 esr_val;u32 esr_mask; +u32 esr_val; +u64 pstate_mask; +u64 pstate_val;int (*fn)(struct pt_regs *regs,unsigned int esr); };arch/arm64/kernel/debug-monitors.c +2
-1@@ -303,7 +303,8 @@ static int call_break_hook(struct pt_regs *regs, unsigned int esr)read_lock (&break_hook_lock);list_for_each_entry (hook, &break_hook, node) -if ((esr & hook->esr_mask) == hook->esr_val) +if ((esr & hook->esr_mask) == hook->esr_val && + (regs->pstate & hook->pstate_mask) == hook->pstate_val) fn = hook->fn;read_unlock (&break_hook_lock);arch/arm64/kernel/kgdb.c +4
-0@@ -236,12 +236,16 @@ static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned int esr)static struct break_hook kgdb_brkpt_hook = { .esr_mask =0xffffffff , .esr_val =DBG_ESR_VAL_BRK (KGDB_DYN_DGB_BRK_IMM ), + .pstate_mask =PSR_MODE_MASK , + .pstate_val = PSR_MODE_EL1h, .fn = kgdb_brk_fn };static struct break_hook kgdb_compiled_brkpt_hook = { .esr_mask =0xffffffff , .esr_val =DBG_ESR_VAL_BRK (KDBG_COMPILED_DBG_BRK_IMM ), + .pstate_mask =PSR_MODE_MASK , + .pstate_val = PSR_MODE_EL1h, .fn = kgdb_compiled_brk_fn };Signature · 1 line
2.0.3
Signature · 7 lines
Hi, all, Resubmitting this from a couple of weeks ago, also fixing the same problem in ARM64. Could I get some feedback or an ack on this? Otherwise, please apply it. Thanks! Omar
Hello,
Quoted 16 lines · Omar Sandoval
The kgdb breakpoint hooks (
kgdb_brk_fnandkgdb_compiled_brk_fn) should only be entered when a kgdb break instruction is executed from the kernel. Otherwise, if kgdb is enabled, a userspace program can cause the kernel to drop into the debugger by executing eitherKGDB_BREAKINSTorKGDB_COMPILED_BREAKon ARM, or brk #KGDB_{DYN,COMPILED}_DGB_BRK_IMM on ARM64.Signed-off-by Omar Sandoval <[email protected]>---
The following program reproduces the fixed problem on ARM:
.globl_start_start:udf #65006 @ KGDB_BREAKINSTAnd on ARM64:
.globl_start_start:
Quoted 7 lines
brk #0x400 @ KGDB_DYN_DGB_BRK_IMMarch/arm/kernel/kgdb.c | 4 ++++ arch/arm64/include/asm/debug-monitors.h | 4 +++- arch/arm64/kernel/debug-monitors.c | 3 ++- arch/arm64/kernel/kgdb.c | 4 ++++ 4 files changed, 13 insertions(+), 2 deletions(-)
Whilst this sounds like a worrying problem, I've failed to reproduce it on arm64. Executing a brk instruction with either KGDB_DYN_DGB_BRK_IMM or KDBG_COMPILED_DBG_BRK_IMM immediates from userspace results in a SIGTRAP being delivered, assumedly because kgdb_handle_exception simply returns when kgdb isn't active.
arch/arm64/include/asm/debug-monitors.h void unregister_step_hook(struct step_hook *hook); · line 111
Quoted 13 lines
arch/arm64/include/asm/debug-monitors.h +3
-1@@ -105,8 +105,10 @@ void unregister_step_hook(struct step_hook *hook);struct break_hook {struct list_head node; -u32 esr_val;u32 esr_mask; +u32 esr_val; +u64 pstate_mask; +u64 pstate_val;
The following (totally untested) diff is simpler for arm64, but again, I'm not sure we even have a problem here.
On which systems have you managed to reproduce this and how?
Will
--->8
arch/arm64/kernel/debug-monitors.c +11 -11
@@ -315,20 +315,20 @@ static int brk_handler(unsigned long addr, unsigned int esr, {siginfo_t info; -if (call_break_hook (regs, esr) ==DBG_HOOK_HANDLED ) -return 0 ; +if (user_mode (regs)) { + info = (siginfo_t ) { + .si_signo =SIGTRAP , + .si_errno =0 , + .si_code =TRAP_BRKPT , + .si_addr = (void __user *)instruction_pointer (regs), + }; -if (!user_mode (regs)) +force_sig_info (SIGTRAP , &info, current); + }else if (call_break_hook (regs, esr) !=DBG_HOOK_HANDLED ) { +pr_warning ("Unexpected kernel BRK exception at EL1\n" );return -EFAULT ; + } - info = (siginfo_t ) { - .si_signo =SIGTRAP , - .si_errno =0 , - .si_code =TRAP_BRKPT , - .si_addr = (void __user *)instruction_pointer (regs), - }; - -force_sig_info (SIGTRAP , &info, current);return 0 ; }
Hi,
Quoted 7 lines · Will Deacon
Whilst this sounds like a worrying problem, I've failed to reproduce it on arm64. Executing a brk instruction with either
KGDB_DYN_DGB_BRK_IMMorKDBG_COMPILED_DBG_BRK_IMMimmediates from userspace results in a SIGTRAP being delivered, assumedly becausekgdb_handle_exceptionsimply returns when kgdb isn't active.From what I can tell, the break hooks are registered so long as kgdb is enabled
at all - i.e., the kernel was compiled with CONFIG_KGDB=y and, for example, CONFIG_KGDB_SERIAL_CONSOLE=y and kgdboc was passed on the kernel command line. kgdb_handle_exception doesn't seem to check whether the debugger is active.
Quoted 2 lines
The following (totally untested) diff is simpler for arm64, but again, I'm not sure we even have a problem here.
This diff also fixes the problem. I don't have a strong preference for either approach, so I can revise the patch with this approach instead if you'd prefer that.
Quoted 1 line
On which systems have you managed to reproduce this and how?
I first reproduced this on a Raspberry Pi. The recommended distro, Raspbian, distributes a kernel compiled with CONFIG_KGDB=y, CONFIG_KGDB_KDB=y, and CONFIG_KDB_KEYBOARD=y, so it was sufficient to have a keyboard plugged in. However, I also reproduced it by booting with kgdboc on the command line, as CONFIG_KGDB_SERIAL_CONSOLE was also enabled. Additionally, I reproduced it and then verified that my patch fixed it on self-compiled kernels.
I don't currenty have access to a physical ARM64 board, so I did all the testing for this with QEMU (loosely following the instructions here:
http://www.bennee.com/~alex/blog/2014/05/09/running-linux-in-qemus-aarch64-system-emulation-mode/).
Again, it was sufficient to build with CONFIG_KGDB=y and CONFIG_KGDB_SERIAL_CONSOLE=y and boot with kgdboc enabled, i.e., passing --append "console=ttyAMA0 kgdboc=ttyAMA0,115200" to QEMU. Here's a snippet from that session.
# ./brk
Entering kdb (current=0xffffffc07df60a80, pid 71) on processor 0 Oops: (null) due to oops @ 0x4000d8
dCPU: 0 PID: 71 Comm: brk Not tainted 3.16.0-rc7ajb #2
dtask: ffffffc07df60a80 ti: ffffffc07d0c0000 task.ti: ffffffc07d0c0000 PC is at 0x4000d8
LR is at 0x0
pc : [<00000000004000d8>] lr : [<0000000000000000>] pstate: 00000000
sp : 0000007fceefcdd0
x29: 0000000000000000 x28: 0000000000000000
x27: 0000000000000000 x26: 0000000000000000
x25: 0000000000000000 x24: 0000000000000000
x23: 0000000000000000 x22: 0000000000000000
x21: 0000000000000000 x20: 0000000000000000
x19: 0000000000000000 x18: 0000000000000000
x17: 0000000000000000 x16: 0000000000000000
x15: 0000000000000000 x14: 0000000000000000
x13: 0000000000000000 x12: 0000000000000000
x11: 0000000000000000 x10: 0000000000000000
x9 : 0000000000000000 x8 : 0000000000000000
x7 : 0000000000000000 x6 : 0000000000000000
x5 : 0000000000000000 x4 : 0000000000000000
x3 : 0000000000000000 x2 : 0000000000000000
x1 : 0000000000000000 x0 : 0000000000000000
Let me know if you still have trouble reproducing this.
Thanks again!
Omar
Quoted 1 line · Omar Sandoval
Hi,
Hi Omar,
Quoted 12 lines · Will Deacon
Quoted 5 lines
Whilst this sounds like a worrying problem, I've failed to reproduce it on arm64. Executing a brk instruction with either
KGDB_DYN_DGB_BRK_IMMorKDBG_COMPILED_DBG_BRK_IMMimmediates from userspace results in a SIGTRAP being delivered, assumedly becausekgdb_handle_exceptionsimply returns when kgdb isn't active.From what I can tell, the break hooks are registered so long as kgdb is enabled at all - i.e., the kernel was compiled with
CONFIG_KGDB=yand, for example,CONFIG_KGDB_SERIAL_CONSOLE=yand kgdboc was passed on the kernel command line.kgdb_handle_exceptiondoesn't seem to check whether the debugger is active.
Quoted 6 lines
Quoted 2 lines
The following (totally untested) diff is simpler for arm64, but again, I'm not sure we even have a problem here.
This diff also fixes the problem. I don't have a strong preference for either approach, so I can revise the patch with this approach instead if you'd prefer that.
I'll merge the arm64 diff I proposed. Could you repost the ARM part please?
Quoted 8 lines
Quoted 1 line
On which systems have you managed to reproduce this and how?
I first reproduced this on a Raspberry Pi. The recommended distro, Raspbian, distributes a kernel compiled with
CONFIG_KGDB=y,CONFIG_KGDB_KDB=y, andCONFIG_KDB_KEYBOARD=y, so it was sufficient to have a keyboard plugged in. However, I also reproduced it by booting with kgdboc on the command line, asCONFIG_KGDB_SERIAL_CONSOLEwas also enabled. Additionally, I reproduced it and then verified that my patch fixed it on self-compiled kernels.
I think enabling and activating kgdb by default is a pretty crazy thing to do, but I agree that we shouldn't allow userspace to trap into it either.
Once you repost the ARM patches, we can look at getting them merged via rmk.
Cheers,
Will
Hi, Will,
Quoted 1 line · Will Deacon
I'll merge the arm64 diff I proposed. Could you repost the ARM part please?
I've just reposted it, hopefully we can get that merged in soon as well.
Quoted 2 lines
I think enabling and activating kgdb by default is a pretty crazy thing to do, but I agree that we shouldn't allow userspace to trap into it either.
Agreed, hopefully the actual footprint of this bug isn't too large, but it's worth fixing anyways.
Quoted 5 lines
Once you repost the ARM patches, we can look at getting them merged via rmk.
Cheers,
Will
Thanks again!
Omar