Commit 8f34a1da35ae ("arm64: ptrace: use HW_BREAKPOINT_EMPTY type for disabled breakpoints") fixed an issue with GDB trying to zero breakpoint control registers. The problem there is that the arch hw_breakpoint code will attempt to create a (disabled), execute breakpoint of length 0. This will fail validation and report unexpected failure to GDB. To avoid this, we treated disabled breakpoints as HW_BREAKPOINT_EMPTY, but that seems to have broken with recent kernels, causing watchpoints to be treated as TYPE_INST in the core code and returning ENOSPC for any further breakpoints. This patch fixes the problem by prioritising the `enable' field of the breakpoint: if it is cleared, we simply update the perf_event_attr to indicate that the thing is disabled and don't bother changing either the type or the length. This reinforces the behaviour that the breakpoint control register is essentially read-only apart from the enable bit when disabling a breakpoint.
Unified · Split
| @@ -214,31 +214,29 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type, · 213 unmodified lines | |||
| 214 | { | 214 | { |
| 215 | int err, len, type, disabled = !ctrl.enabled; | 215 | int err, len, type, disabled = !ctrl.enabled; |
| 216 | 216 | ||
| 217 | if (disabled) { | 217 | attr->disabled = disabled; |
| 218 | len = 0; | 218 | if (disabled) |
| 219 | type = HW_BREAKPOINT_EMPTY; | 219 | return 0; |
| 220 | } else { | 220 | |
| 221 | err = arch_bp_generic_fields(ctrl, &len, &type); | 221 | err = arch_bp_generic_fields(ctrl, &len, &type); |
| 222 | if (err) | 222 | if (err) |
| 223 | return err; | 223 | return err; |
| 224 | 224 | ||
| 225 | switch (note_type) { | 225 | switch (note_type) { |
| 226 | case NT_ARM_HW_BREAK: | 226 | case NT_ARM_HW_BREAK: |
| 227 | if ((type & HW_BREAKPOINT_X) != type) | 227 | if ((type & HW_BREAKPOINT_X) != type) |
| 228 | return -EINVAL; | ||
| 229 | break; | ||
| 230 | case NT_ARM_HW_WATCH: | ||
| 231 | if ((type & HW_BREAKPOINT_RW) != type) | ||
| 232 | return -EINVAL; | ||
| 233 | break; | ||
| 234 | default: | ||
| 235 | return -EINVAL; | 228 | return -EINVAL; |
| 236 | } | 229 | break; |
| 230 | case NT_ARM_HW_WATCH: | ||
| 231 | if ((type & HW_BREAKPOINT_RW) != type) | ||
| 232 | return -EINVAL; | ||
| 233 | break; | ||
| 234 | default: | ||
| 235 | return -EINVAL; | ||
| 237 | } | 236 | } |
| 238 | 237 | ||
| 239 | attr->bp_len = len; | 238 | attr->bp_len = len; |
| 240 | attr->bp_type = type; | 239 | attr->bp_type = type; |
| 241 | attr->disabled = disabled; | ||
| 242 | 240 | ||
| 243 | return 0; | 241 | return 0; |
| 244 | } | 242 | } |