Skip to content

Commit dd135ff

Browse files
b10902118intel-lab-lkp
authored andcommitted
arm64: ptrace: fix hw_break_set() to set addr and ctrl together
This patch fixes the failure of PTRACE_SETREGSET when setting a hardware breakpoint on a non-4-byte aligned address with a valid control to a 32-bit tracee. The issue was discovered while testing LLDB. Link: llvm/llvm-project#152284 The failure happens because hw_break_set() checks and sets the breakpoint address and control separately. This can result in an check failure when it first validates the address to be set with old control. For example, the control are initialized with breakpoint length of 4. Combining with a non-4-byte aligned address would cross a 4-byte boundary, which is invalid. However, the user-provided control may actually specify a length of 1, which should be valid. The fix is to set the address and control together. This is supported by modify_user_hw_breakpoint(), the function that sets and checks breakpoints. The original implementation wrap this function to ptrace_hbp_set_addr() and ptrace_hbp_set_ctrl() for 32-bit API PTRACE_SETHBPREGS simply because it can only modify one register (address or control) per call. For the 64-bit PTRACE_SETREGSET API, this restriction does not apply, so a new helper function ptrace_hbp_set() was added to set both in a single call to modify_user_hw_breakpoint(). For reference, the check is in arch/arm64/kernel/hw_breakpoint.c:hw_breakpoint_arch_parse() which is called via: modify_user_hw_breakpoint() -> modify_user_hw_breakpoint_check() -> hw_breakpoint_parse() -> hw_breakpoint_arch_parse() Signed-off-by: Bill Tsui <[email protected]>
1 parent f620d66 commit dd135ff

File tree

1 file changed

+31
-5
lines changed

1 file changed

+31
-5
lines changed

arch/arm64/kernel/ptrace.c

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,34 @@ static int ptrace_hbp_set_addr(unsigned int note_type,
467467
return err;
468468
}
469469

470+
/* Set the address and control together for non-compat ptrace */
471+
static int ptrace_hbp_set(unsigned int note_type,
472+
struct task_struct *tsk,
473+
unsigned long idx,
474+
u64 addr, u32 uctrl)
475+
{
476+
int err;
477+
struct perf_event *bp;
478+
struct perf_event_attr attr;
479+
struct arch_hw_breakpoint_ctrl ctrl;
480+
481+
bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx);
482+
if (IS_ERR(bp)) {
483+
err = PTR_ERR(bp);
484+
return err;
485+
}
486+
487+
attr = bp->attr;
488+
attr.bp_addr = addr;
489+
490+
decode_ctrl_reg(uctrl, &ctrl);
491+
err = ptrace_hbp_fill_attr_ctrl(note_type, ctrl, &attr);
492+
if (err)
493+
return err;
494+
495+
return modify_user_hw_breakpoint(bp, &attr);
496+
}
497+
470498
#define PTRACE_HBP_ADDR_SZ sizeof(u64)
471499
#define PTRACE_HBP_CTRL_SZ sizeof(u32)
472500
#define PTRACE_HBP_PAD_SZ sizeof(u32)
@@ -524,9 +552,6 @@ static int hw_break_set(struct task_struct *target,
524552
return -EINVAL;
525553
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &addr,
526554
offset, offset + PTRACE_HBP_ADDR_SZ);
527-
if (ret)
528-
return ret;
529-
ret = ptrace_hbp_set_addr(note_type, target, idx, addr);
530555
if (ret)
531556
return ret;
532557
offset += PTRACE_HBP_ADDR_SZ;
@@ -537,10 +562,11 @@ static int hw_break_set(struct task_struct *target,
537562
offset, offset + PTRACE_HBP_CTRL_SZ);
538563
if (ret)
539564
return ret;
540-
ret = ptrace_hbp_set_ctrl(note_type, target, idx, ctrl);
565+
offset += PTRACE_HBP_CTRL_SZ;
566+
567+
ret = ptrace_hbp_set(note_type, target, idx, addr, ctrl);
541568
if (ret)
542569
return ret;
543-
offset += PTRACE_HBP_CTRL_SZ;
544570

545571
user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
546572
offset, offset + PTRACE_HBP_PAD_SZ);

0 commit comments

Comments
 (0)