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 | 215 | int err, len, type, disabled = !ctrl.enabled; |
| 216 | 216 | |
| 217 | − if (disabled) { | |
| 218 | − len = 0; | |
| 219 | − type = HW_BREAKPOINT_EMPTY; | |
| 220 | − } else { | |
| 221 | − err = arch_bp_generic_fields(ctrl, &len, &type); | |
| 222 | − if (err) | |
| 223 | − return err; | |
| 224 | − | |
| 225 | − switch (note_type) { | |
| 226 | − case NT_ARM_HW_BREAK: | |
| 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: | |
| 217 | + attr->disabled = disabled; | |
| 218 | + if (disabled) | |
| 219 | + return 0; | |
| 220 | + | |
| 221 | + err = arch_bp_generic_fields(ctrl, &len, &type); | |
| 222 | + if (err) | |
| 223 | + return err; | |
| 224 | + | |
| 225 | + switch (note_type) { | |
| 226 | + case NT_ARM_HW_BREAK: | |
| 227 | + if ((type & HW_BREAKPOINT_X) != type) | |
| 235 | 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 | 238 | attr->bp_len = len; |
| 240 | 239 | attr->bp_type = type; |
| 241 | − attr->disabled = disabled; | |
| 242 | 240 | |
| 243 | 241 | return 0; |
| 244 | 242 | } |