powerpc/watchpoint: Fix ptrace code that muck around with address/len
authorRavi Bangoria <ravi.bangoria@linux.ibm.com>
Thu, 17 Oct 2019 09:32:00 +0000 (15:02 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Wed, 13 Nov 2019 05:58:03 +0000 (16:58 +1100)
ptrace_set_debugreg() does not consider new length while overwriting
the watchpoint. Fix that. ppc_set_hwdebug() aligns watchpoint address
to doubleword boundary but does not change the length. If address
range is crossing doubleword boundary and length is less then 8, we
will lose samples from second doubleword. So fix that as well.

Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20191017093204.7511-4-ravi.bangoria@linux.ibm.com
arch/powerpc/include/asm/hw_breakpoint.h
arch/powerpc/kernel/ptrace.c

index ea91ac7f5a273d31da3c0bd675892008256130ec..27ac6f5d28918b3f8a00533c3bfe1296c04dc8ee 100644 (file)
@@ -34,6 +34,8 @@ struct arch_hw_breakpoint {
 #define HW_BRK_TYPE_PRIV_ALL   (HW_BRK_TYPE_USER | HW_BRK_TYPE_KERNEL | \
                                 HW_BRK_TYPE_HYP)
 
+#define HW_BREAKPOINT_ALIGN 0x7
+
 #define DABR_MAX_LEN   8
 #define DAWR_MAX_LEN   512
 
@@ -48,8 +50,6 @@ struct pmu;
 struct perf_sample_data;
 struct task_struct;
 
-#define HW_BREAKPOINT_ALIGN 0x7
-
 extern int hw_breakpoint_slots(int type);
 extern int arch_bp_generic_fields(int type, int *gen_bp_type);
 extern int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw);
index c2dc93157b991b38baf48eb20fd54f1edd7de8ef..25c0424e8868b9c753a11cdd00e79aee9f6dc931 100644 (file)
@@ -2440,6 +2440,7 @@ static int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
        if (bp) {
                attr = bp->attr;
                attr.bp_addr = hw_brk.address;
+               attr.bp_len = DABR_MAX_LEN;
                arch_bp_generic_fields(hw_brk.type, &attr.bp_type);
 
                /* Enable breakpoint */
@@ -2881,7 +2882,7 @@ static long ppc_set_hwdebug(struct task_struct *child,
        if ((unsigned long)bp_info->addr >= TASK_SIZE)
                return -EIO;
 
-       brk.address = bp_info->addr & ~7UL;
+       brk.address = bp_info->addr & ~HW_BREAKPOINT_ALIGN;
        brk.type = HW_BRK_TYPE_TRANSLATE;
        brk.len = DABR_MAX_LEN;
        if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ)
@@ -2889,10 +2890,6 @@ static long ppc_set_hwdebug(struct task_struct *child,
        if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)
                brk.type |= HW_BRK_TYPE_WRITE;
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
-       /*
-        * Check if the request is for 'range' breakpoints. We can
-        * support it if range < 8 bytes.
-        */
        if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE)
                len = bp_info->addr2 - bp_info->addr;
        else if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_EXACT)
@@ -2905,7 +2902,7 @@ static long ppc_set_hwdebug(struct task_struct *child,
 
        /* Create a new breakpoint request if one doesn't exist already */
        hw_breakpoint_init(&attr);
-       attr.bp_addr = (unsigned long)bp_info->addr & ~HW_BREAKPOINT_ALIGN;
+       attr.bp_addr = (unsigned long)bp_info->addr;
        attr.bp_len = len;
        arch_bp_generic_fields(brk.type, &attr.bp_type);