The root cause is that ptrace() does not validate either
address or size when setting a hardware watchpoint/breakpoint.
As a result, watchpoints were set at address 0, the initial value of
aarch64_debug_reg_state in aarch64_process_info, when the
PTRACE_SETREGSET request was first made in
aarch64_linux_set_debug_regs(), in preparation for resuming the thread.
Other than changing the kernel ptrace() implementation, first attempt to
fix this problem in gdb was to focus on aarch64_linux_new_thread().
Instead of marking all hardware breakpoint/watchpoint register pairs for
the new thread that have changed, tried to reflect the state by using
either DR_MARK_ALL_CHANGED() if they have really been changed or
DR_CLEAR_CHANGED() otherwise. But finding whether or not the registers
have been changed by using parent's lwp_info or aarch64_process_info
proved to be hard or incorrect, especially the latter which caused
gdbserver to crash in the middle of the ptid_of_lwp() call.
Another approach was then taken - add function initial_control_length()
to validate the contents in the control registers, basing on the fact that
the kernel only supports Byte Address Select (BAS) values of 0x1, 0x3, 0xf
and 0xff, before calling ptrace() in aarch64_linux_set_debug_regs().
Tested on aarch64-linux-gnu. No regressions.
---
gdb/ChangeLog | 7 +++++++
gdb/nat/aarch64-linux-hw-point.c | 18 +++++++++++++++++-
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index c4f55a8137..543e1a0487 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,10 @@
+2017-10-06 Weimin Pan <weimin.pan@oracle.com>
+
+ PR gdb/21870
+ * nat/aarch64-linux-hw-point.c (aarch64_linux_set_debug_regs):
+ Call new function to validate the length in control registers.
+ (initial_control_length): New function.
+
2017-09-15 Pedro Alves <palves@redhat.com>
* compile/compile-c-types.c (convert_enum, convert_int)
diff --git a/gdb/nat/aarch64-linux-hw-point.c b/gdb/nat/aarch64-linux-hw-point.c
index 9800d9a59c..22c0a48c14 100644
--- a/gdb/nat/aarch64-linux-hw-point.c
+++ b/gdb/nat/aarch64-linux-hw-point.c
@@ -548,6 +548,22 @@ aarch64_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
state);
}
+/* Validate the lengths of breakpoints/watchpoints, according to the
+ contents of these hardware debug control registers, and return
+ true if all these registers contain zero length. */
+
+static bool
+initial_control_length (const unsigned int *ctrl, int count)
+{
+ for (int i = 0; i < count; i++)
+ {
+ if (DR_CONTROL_LENGTH (ctrl[i]))
+ return false;
+ }
+
+ return true;
+}
+
/* Call ptrace to set the thread TID's hardware breakpoint/watchpoint
registers with data from *STATE. */
@@ -566,7 +582,7 @@ aarch64_linux_set_debug_regs (const struct aarch64_debug_reg_state *state,
count = watchpoint ? aarch64_num_wp_regs : aarch64_num_bp_regs;
addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
- if (count == 0)
+ if (count == 0 || initial_control_length (ctrl, count))
return;
iov.iov_len = (offsetof (struct user_hwdebug_state, dbg_regs)
+ count * sizeof (regs.dbg_regs[0]));