Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: debug: call software breakpoint handlers statically

Software breakpoints pass an immediate value in ESR ("comment") that can
be used to call a specialized handler (KGDB, KASAN...).
We do so in two different ways :
- During early boot, `early_brk64` statically checks against known
immediates and calls the corresponding handler,
- During init, handlers are dynamically registered into a list. When
called, the generic software breakpoint handler will iterate over
the list to find the appropriate handler.

The dynamic registration does not provide any benefit here as it is not
exported and all its uses are within the arm64 tree. It also depends on an
RCU list, whose safe access currently relies on the non-preemptible state
of `do_debug_exception`.

Replace the list iteration logic in `call_break_hooks` to call
the breakpoint handlers statically if they are enabled, like in
`early_brk64`.
Expose the handlers in their respective headers to be reachable from
`arch/arm64/kernel/debug-monitors.c` at link time.

Unify the naming of the software breakpoint handlers to XXX_brk_handler(),
making it clear they are related and to differentiate from the
hardware breakpoints.

Signed-off-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
Tested-by: Luis Claudio R. Goncalves <lgoncalv@redhat.com>
Reviewed-by: Will Deacon <will@kernel.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Link: https://lore.kernel.org/r/20250707114109.35672-4-ada.coupriediaz@arm.com
Signed-off-by: Will Deacon <will@kernel.org>

authored by

Ada Couprie Diaz and committed by
Will Deacon
6adfdc5e b1e2d955

+83 -113
+3
arch/arm64/include/asm/kgdb.h
··· 24 24 extern void kgdb_handle_bus_error(void); 25 25 extern int kgdb_fault_expected; 26 26 27 + int kgdb_brk_handler(struct pt_regs *regs, unsigned long esr); 28 + int kgdb_compiled_brk_handler(struct pt_regs *regs, unsigned long esr); 29 + 27 30 #endif /* !__ASSEMBLY__ */ 28 31 29 32 /*
+8
arch/arm64/include/asm/kprobes.h
··· 41 41 void __kprobes *trampoline_probe_handler(struct pt_regs *regs); 42 42 43 43 #endif /* CONFIG_KPROBES */ 44 + 45 + int __kprobes kprobe_brk_handler(struct pt_regs *regs, 46 + unsigned long esr); 47 + int __kprobes kprobe_ss_brk_handler(struct pt_regs *regs, 48 + unsigned long esr); 49 + int __kprobes kretprobe_brk_handler(struct pt_regs *regs, 50 + unsigned long esr); 51 + 44 52 #endif /* _ARM_KPROBES_H */
+6
arch/arm64/include/asm/traps.h
··· 29 29 void arm64_force_sig_mceerr(int code, unsigned long far, short lsb, const char *str); 30 30 void arm64_force_sig_ptrace_errno_trap(int errno, unsigned long far, const char *str); 31 31 32 + int bug_brk_handler(struct pt_regs *regs, unsigned long esr); 33 + int cfi_brk_handler(struct pt_regs *regs, unsigned long esr); 34 + int reserved_fault_brk_handler(struct pt_regs *regs, unsigned long esr); 35 + int kasan_brk_handler(struct pt_regs *regs, unsigned long esr); 36 + int ubsan_brk_handler(struct pt_regs *regs, unsigned long esr); 37 + 32 38 int early_brk64(unsigned long addr, unsigned long esr, struct pt_regs *regs); 33 39 34 40 /*
+2
arch/arm64/include/asm/uprobes.h
··· 28 28 bool simulate; 29 29 }; 30 30 31 + int uprobe_brk_handler(struct pt_regs *regs, unsigned long esr); 32 + 31 33 #endif
+42 -12
arch/arm64/kernel/debug-monitors.c
··· 21 21 #include <asm/cputype.h> 22 22 #include <asm/daifflags.h> 23 23 #include <asm/debug-monitors.h> 24 + #include <asm/kgdb.h> 25 + #include <asm/kprobes.h> 24 26 #include <asm/system_misc.h> 25 27 #include <asm/traps.h> 28 + #include <asm/uprobes.h> 26 29 27 30 /* Determine debug architecture. */ 28 31 u8 debug_monitors_arch(void) ··· 302 299 303 300 static int call_break_hook(struct pt_regs *regs, unsigned long esr) 304 301 { 305 - struct break_hook *hook; 306 - struct list_head *list; 307 - 308 - list = user_mode(regs) ? &user_break_hook : &kernel_break_hook; 309 - 310 - /* 311 - * Since brk exception disables interrupt, this function is 312 - * entirely not preemptible, and we can use rcu list safely here. 313 - */ 314 - list_for_each_entry_rcu(hook, list, node) { 315 - if ((esr_brk_comment(esr) & ~hook->mask) == hook->imm) 316 - return hook->fn(regs, esr); 302 + if (user_mode(regs)) { 303 + if (IS_ENABLED(CONFIG_UPROBES) && 304 + esr_brk_comment(esr) == UPROBES_BRK_IMM) 305 + return uprobe_brk_handler(regs, esr); 306 + return DBG_HOOK_ERROR; 317 307 } 308 + 309 + if (esr_brk_comment(esr) == BUG_BRK_IMM) 310 + return bug_brk_handler(regs, esr); 311 + 312 + if (IS_ENABLED(CONFIG_CFI_CLANG) && esr_is_cfi_brk(esr)) 313 + return cfi_brk_handler(regs, esr); 314 + 315 + if (esr_brk_comment(esr) == FAULT_BRK_IMM) 316 + return reserved_fault_brk_handler(regs, esr); 317 + 318 + if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) && 319 + (esr_brk_comment(esr) & ~KASAN_BRK_MASK) == KASAN_BRK_IMM) 320 + return kasan_brk_handler(regs, esr); 321 + 322 + if (IS_ENABLED(CONFIG_UBSAN_TRAP) && esr_is_ubsan_brk(esr)) 323 + return ubsan_brk_handler(regs, esr); 324 + 325 + if (IS_ENABLED(CONFIG_KGDB)) { 326 + if (esr_brk_comment(esr) == KGDB_DYN_DBG_BRK_IMM) 327 + return kgdb_brk_handler(regs, esr); 328 + if (esr_brk_comment(esr) == KGDB_COMPILED_DBG_BRK_IMM) 329 + return kgdb_compiled_brk_handler(regs, esr); 330 + } 331 + 332 + if (IS_ENABLED(CONFIG_KPROBES)) { 333 + if (esr_brk_comment(esr) == KPROBES_BRK_IMM) 334 + return kprobe_brk_handler(regs, esr); 335 + if (esr_brk_comment(esr) == KPROBES_BRK_SS_IMM) 336 + return kprobe_ss_brk_handler(regs, esr); 337 + } 338 + 339 + if (IS_ENABLED(CONFIG_KRETPROBES) && 340 + esr_brk_comment(esr) == KRETPROBES_BRK_IMM) 341 + return kretprobe_brk_handler(regs, esr); 318 342 319 343 return DBG_HOOK_ERROR; 320 344 }
+4 -18
arch/arm64/kernel/kgdb.c
··· 234 234 return err; 235 235 } 236 236 237 - static int kgdb_brk_fn(struct pt_regs *regs, unsigned long esr) 237 + int kgdb_brk_handler(struct pt_regs *regs, unsigned long esr) 238 238 { 239 239 kgdb_handle_exception(1, SIGTRAP, 0, regs); 240 240 return DBG_HOOK_HANDLED; 241 241 } 242 - NOKPROBE_SYMBOL(kgdb_brk_fn) 242 + NOKPROBE_SYMBOL(kgdb_brk_handler) 243 243 244 - static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned long esr) 244 + int kgdb_compiled_brk_handler(struct pt_regs *regs, unsigned long esr) 245 245 { 246 246 compiled_break = 1; 247 247 kgdb_handle_exception(1, SIGTRAP, 0, regs); 248 248 249 249 return DBG_HOOK_HANDLED; 250 250 } 251 - NOKPROBE_SYMBOL(kgdb_compiled_brk_fn); 251 + NOKPROBE_SYMBOL(kgdb_compiled_brk_handler); 252 252 253 253 static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned long esr) 254 254 { ··· 259 259 return DBG_HOOK_HANDLED; 260 260 } 261 261 NOKPROBE_SYMBOL(kgdb_step_brk_fn); 262 - 263 - static struct break_hook kgdb_brkpt_hook = { 264 - .fn = kgdb_brk_fn, 265 - .imm = KGDB_DYN_DBG_BRK_IMM, 266 - }; 267 - 268 - static struct break_hook kgdb_compiled_brkpt_hook = { 269 - .fn = kgdb_compiled_brk_fn, 270 - .imm = KGDB_COMPILED_DBG_BRK_IMM, 271 - }; 272 262 273 263 static struct step_hook kgdb_step_hook = { 274 264 .fn = kgdb_step_brk_fn ··· 306 316 if (ret != 0) 307 317 return ret; 308 318 309 - register_kernel_break_hook(&kgdb_brkpt_hook); 310 - register_kernel_break_hook(&kgdb_compiled_brkpt_hook); 311 319 register_kernel_step_hook(&kgdb_step_hook); 312 320 return 0; 313 321 } ··· 317 329 */ 318 330 void kgdb_arch_exit(void) 319 331 { 320 - unregister_kernel_break_hook(&kgdb_brkpt_hook); 321 - unregister_kernel_break_hook(&kgdb_compiled_brkpt_hook); 322 332 unregister_kernel_step_hook(&kgdb_step_hook); 323 333 unregister_die_notifier(&kgdb_notifier); 324 334 }
+6 -25
arch/arm64/kernel/probes/kprobes.c
··· 292 292 return 0; 293 293 } 294 294 295 - static int __kprobes 296 - kprobe_breakpoint_handler(struct pt_regs *regs, unsigned long esr) 295 + int __kprobes 296 + kprobe_brk_handler(struct pt_regs *regs, unsigned long esr) 297 297 { 298 298 struct kprobe *p, *cur_kprobe; 299 299 struct kprobe_ctlblk *kcb; ··· 336 336 return DBG_HOOK_HANDLED; 337 337 } 338 338 339 - static struct break_hook kprobes_break_hook = { 340 - .imm = KPROBES_BRK_IMM, 341 - .fn = kprobe_breakpoint_handler, 342 - }; 343 - 344 - static int __kprobes 345 - kprobe_breakpoint_ss_handler(struct pt_regs *regs, unsigned long esr) 339 + int __kprobes 340 + kprobe_ss_brk_handler(struct pt_regs *regs, unsigned long esr) 346 341 { 347 342 struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); 348 343 unsigned long addr = instruction_pointer(regs); ··· 355 360 return DBG_HOOK_ERROR; 356 361 } 357 362 358 - static struct break_hook kprobes_break_ss_hook = { 359 - .imm = KPROBES_BRK_SS_IMM, 360 - .fn = kprobe_breakpoint_ss_handler, 361 - }; 362 - 363 - static int __kprobes 364 - kretprobe_breakpoint_handler(struct pt_regs *regs, unsigned long esr) 363 + int __kprobes 364 + kretprobe_brk_handler(struct pt_regs *regs, unsigned long esr) 365 365 { 366 366 if (regs->pc != (unsigned long)__kretprobe_trampoline) 367 367 return DBG_HOOK_ERROR; ··· 364 374 regs->pc = kretprobe_trampoline_handler(regs, (void *)regs->regs[29]); 365 375 return DBG_HOOK_HANDLED; 366 376 } 367 - 368 - static struct break_hook kretprobes_break_hook = { 369 - .imm = KRETPROBES_BRK_IMM, 370 - .fn = kretprobe_breakpoint_handler, 371 - }; 372 377 373 378 /* 374 379 * Provide a blacklist of symbols identifying ranges which cannot be kprobed. ··· 407 422 408 423 int __init arch_init_kprobes(void) 409 424 { 410 - register_kernel_break_hook(&kprobes_break_hook); 411 - register_kernel_break_hook(&kprobes_break_ss_hook); 412 - register_kernel_break_hook(&kretprobes_break_hook); 413 - 414 425 return 0; 415 426 }
+1 -1
arch/arm64/kernel/probes/kprobes_trampoline.S
··· 12 12 SYM_CODE_START(__kretprobe_trampoline) 13 13 /* 14 14 * Trigger a breakpoint exception. The PC will be adjusted by 15 - * kretprobe_breakpoint_handler(), and no subsequent instructions will 15 + * kretprobe_brk_handler(), and no subsequent instructions will 16 16 * be executed from the trampoline. 17 17 */ 18 18 brk #KRETPROBES_BRK_IMM
+1 -8
arch/arm64/kernel/probes/uprobes.c
··· 173 173 return NOTIFY_DONE; 174 174 } 175 175 176 - static int uprobe_breakpoint_handler(struct pt_regs *regs, 176 + int uprobe_brk_handler(struct pt_regs *regs, 177 177 unsigned long esr) 178 178 { 179 179 if (uprobe_pre_sstep_notifier(regs)) ··· 194 194 return DBG_HOOK_ERROR; 195 195 } 196 196 197 - /* uprobe breakpoint handler hook */ 198 - static struct break_hook uprobes_break_hook = { 199 - .imm = UPROBES_BRK_IMM, 200 - .fn = uprobe_breakpoint_handler, 201 - }; 202 - 203 197 /* uprobe single step handler hook */ 204 198 static struct step_hook uprobes_step_hook = { 205 199 .fn = uprobe_single_step_handler, ··· 201 207 202 208 static int __init arch_init_uprobes(void) 203 209 { 204 - register_user_break_hook(&uprobes_break_hook); 205 210 register_user_step_hook(&uprobes_step_hook); 206 211 207 212 return 0;
+10 -49
arch/arm64/kernel/traps.c
··· 987 987 int is_valid_bugaddr(unsigned long addr) 988 988 { 989 989 /* 990 - * bug_handler() only called for BRK #BUG_BRK_IMM. 990 + * bug_brk_handler() only called for BRK #BUG_BRK_IMM. 991 991 * So the answer is trivial -- any spurious instances with no 992 992 * bug table entry will be rejected by report_bug() and passed 993 993 * back to the debug-monitors code and handled as a fatal ··· 997 997 } 998 998 #endif 999 999 1000 - static int bug_handler(struct pt_regs *regs, unsigned long esr) 1000 + int bug_brk_handler(struct pt_regs *regs, unsigned long esr) 1001 1001 { 1002 1002 switch (report_bug(regs->pc, regs)) { 1003 1003 case BUG_TRAP_TYPE_BUG: ··· 1017 1017 return DBG_HOOK_HANDLED; 1018 1018 } 1019 1019 1020 - static struct break_hook bug_break_hook = { 1021 - .fn = bug_handler, 1022 - .imm = BUG_BRK_IMM, 1023 - }; 1024 - 1025 1020 #ifdef CONFIG_CFI_CLANG 1026 - static int cfi_handler(struct pt_regs *regs, unsigned long esr) 1021 + int cfi_brk_handler(struct pt_regs *regs, unsigned long esr) 1027 1022 { 1028 1023 unsigned long target; 1029 1024 u32 type; ··· 1041 1046 arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE); 1042 1047 return DBG_HOOK_HANDLED; 1043 1048 } 1044 - 1045 - static struct break_hook cfi_break_hook = { 1046 - .fn = cfi_handler, 1047 - .imm = CFI_BRK_IMM_BASE, 1048 - .mask = CFI_BRK_IMM_MASK, 1049 - }; 1050 1049 #endif /* CONFIG_CFI_CLANG */ 1051 1050 1052 - static int reserved_fault_handler(struct pt_regs *regs, unsigned long esr) 1051 + int reserved_fault_brk_handler(struct pt_regs *regs, unsigned long esr) 1053 1052 { 1054 1053 pr_err("%s generated an invalid instruction at %pS!\n", 1055 1054 "Kernel text patching", ··· 1053 1064 return DBG_HOOK_ERROR; 1054 1065 } 1055 1066 1056 - static struct break_hook fault_break_hook = { 1057 - .fn = reserved_fault_handler, 1058 - .imm = FAULT_BRK_IMM, 1059 - }; 1060 - 1061 1067 #ifdef CONFIG_KASAN_SW_TAGS 1062 1068 1063 1069 #define KASAN_ESR_RECOVER 0x20 ··· 1060 1076 #define KASAN_ESR_SIZE_MASK 0x0f 1061 1077 #define KASAN_ESR_SIZE(esr) (1 << ((esr) & KASAN_ESR_SIZE_MASK)) 1062 1078 1063 - static int kasan_handler(struct pt_regs *regs, unsigned long esr) 1079 + int kasan_brk_handler(struct pt_regs *regs, unsigned long esr) 1064 1080 { 1065 1081 bool recover = esr & KASAN_ESR_RECOVER; 1066 1082 bool write = esr & KASAN_ESR_WRITE; ··· 1091 1107 arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE); 1092 1108 return DBG_HOOK_HANDLED; 1093 1109 } 1094 - 1095 - static struct break_hook kasan_break_hook = { 1096 - .fn = kasan_handler, 1097 - .imm = KASAN_BRK_IMM, 1098 - .mask = KASAN_BRK_MASK, 1099 - }; 1100 1110 #endif 1101 1111 1102 1112 #ifdef CONFIG_UBSAN_TRAP 1103 - static int ubsan_handler(struct pt_regs *regs, unsigned long esr) 1113 + int ubsan_brk_handler(struct pt_regs *regs, unsigned long esr) 1104 1114 { 1105 1115 die(report_ubsan_failure(esr & UBSAN_BRK_MASK), regs, esr); 1106 1116 return DBG_HOOK_HANDLED; 1107 1117 } 1108 - 1109 - static struct break_hook ubsan_break_hook = { 1110 - .fn = ubsan_handler, 1111 - .imm = UBSAN_BRK_IMM, 1112 - .mask = UBSAN_BRK_MASK, 1113 - }; 1114 1118 #endif 1115 1119 1116 1120 /* ··· 1110 1138 { 1111 1139 #ifdef CONFIG_CFI_CLANG 1112 1140 if (esr_is_cfi_brk(esr)) 1113 - return cfi_handler(regs, esr) != DBG_HOOK_HANDLED; 1141 + return cfi_brk_handler(regs, esr) != DBG_HOOK_HANDLED; 1114 1142 #endif 1115 1143 #ifdef CONFIG_KASAN_SW_TAGS 1116 1144 if ((esr_brk_comment(esr) & ~KASAN_BRK_MASK) == KASAN_BRK_IMM) 1117 - return kasan_handler(regs, esr) != DBG_HOOK_HANDLED; 1145 + return kasan_brk_handler(regs, esr) != DBG_HOOK_HANDLED; 1118 1146 #endif 1119 1147 #ifdef CONFIG_UBSAN_TRAP 1120 1148 if (esr_is_ubsan_brk(esr)) 1121 - return ubsan_handler(regs, esr) != DBG_HOOK_HANDLED; 1149 + return ubsan_brk_handler(regs, esr) != DBG_HOOK_HANDLED; 1122 1150 #endif 1123 - return bug_handler(regs, esr) != DBG_HOOK_HANDLED; 1151 + return bug_brk_handler(regs, esr) != DBG_HOOK_HANDLED; 1124 1152 } 1125 1153 1126 1154 void __init trap_init(void) 1127 1155 { 1128 - register_kernel_break_hook(&bug_break_hook); 1129 - #ifdef CONFIG_CFI_CLANG 1130 - register_kernel_break_hook(&cfi_break_hook); 1131 - #endif 1132 - register_kernel_break_hook(&fault_break_hook); 1133 - #ifdef CONFIG_KASAN_SW_TAGS 1134 - register_kernel_break_hook(&kasan_break_hook); 1135 - #endif 1136 - #ifdef CONFIG_UBSAN_TRAP 1137 - register_kernel_break_hook(&ubsan_break_hook); 1138 - #endif 1139 1156 debug_traps_init(); 1140 1157 }