Skip to content

Commit 6adfdc5

Browse files
Ada Couprie Diazwilldeacon
authored andcommitted
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 <[email protected]> Tested-by: Luis Claudio R. Goncalves <[email protected]> Reviewed-by: Will Deacon <[email protected]> Acked-by: Mark Rutland <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent b1e2d95 commit 6adfdc5

File tree

10 files changed

+81
-111
lines changed

10 files changed

+81
-111
lines changed

arch/arm64/include/asm/kgdb.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ static inline void arch_kgdb_breakpoint(void)
2424
extern void kgdb_handle_bus_error(void);
2525
extern int kgdb_fault_expected;
2626

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+
2730
#endif /* !__ASSEMBLY__ */
2831

2932
/*

arch/arm64/include/asm/kprobes.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,12 @@ void __kretprobe_trampoline(void);
4141
void __kprobes *trampoline_probe_handler(struct pt_regs *regs);
4242

4343
#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+
4452
#endif /* _ARM_KPROBES_H */

arch/arm64/include/asm/traps.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ void arm64_force_sig_fault_pkey(unsigned long far, const char *str, int pkey);
2929
void arm64_force_sig_mceerr(int code, unsigned long far, short lsb, const char *str);
3030
void arm64_force_sig_ptrace_errno_trap(int errno, unsigned long far, const char *str);
3131

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+
3238
int early_brk64(unsigned long addr, unsigned long esr, struct pt_regs *regs);
3339

3440
/*

arch/arm64/include/asm/uprobes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,6 @@ struct arch_uprobe {
2828
bool simulate;
2929
};
3030

31+
int uprobe_brk_handler(struct pt_regs *regs, unsigned long esr);
32+
3133
#endif

arch/arm64/kernel/debug-monitors.c

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@
2121
#include <asm/cputype.h>
2222
#include <asm/daifflags.h>
2323
#include <asm/debug-monitors.h>
24+
#include <asm/kgdb.h>
25+
#include <asm/kprobes.h>
2426
#include <asm/system_misc.h>
2527
#include <asm/traps.h>
28+
#include <asm/uprobes.h>
2629

2730
/* Determine debug architecture. */
2831
u8 debug_monitors_arch(void)
@@ -299,20 +302,47 @@ void unregister_kernel_break_hook(struct break_hook *hook)
299302

300303
static int call_break_hook(struct pt_regs *regs, unsigned long esr)
301304
{
302-
struct break_hook *hook;
303-
struct list_head *list;
305+
if (user_mode(regs)) {
306+
if (IS_ENABLED(CONFIG_UPROBES) &&
307+
esr_brk_comment(esr) == UPROBES_BRK_IMM)
308+
return uprobe_brk_handler(regs, esr);
309+
return DBG_HOOK_ERROR;
310+
}
304311

305-
list = user_mode(regs) ? &user_break_hook : &kernel_break_hook;
312+
if (esr_brk_comment(esr) == BUG_BRK_IMM)
313+
return bug_brk_handler(regs, esr);
306314

307-
/*
308-
* Since brk exception disables interrupt, this function is
309-
* entirely not preemptible, and we can use rcu list safely here.
310-
*/
311-
list_for_each_entry_rcu(hook, list, node) {
312-
if ((esr_brk_comment(esr) & ~hook->mask) == hook->imm)
313-
return hook->fn(regs, esr);
315+
if (IS_ENABLED(CONFIG_CFI_CLANG) && esr_is_cfi_brk(esr))
316+
return cfi_brk_handler(regs, esr);
317+
318+
if (esr_brk_comment(esr) == FAULT_BRK_IMM)
319+
return reserved_fault_brk_handler(regs, esr);
320+
321+
if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) &&
322+
(esr_brk_comment(esr) & ~KASAN_BRK_MASK) == KASAN_BRK_IMM)
323+
return kasan_brk_handler(regs, esr);
324+
325+
if (IS_ENABLED(CONFIG_UBSAN_TRAP) && esr_is_ubsan_brk(esr))
326+
return ubsan_brk_handler(regs, esr);
327+
328+
if (IS_ENABLED(CONFIG_KGDB)) {
329+
if (esr_brk_comment(esr) == KGDB_DYN_DBG_BRK_IMM)
330+
return kgdb_brk_handler(regs, esr);
331+
if (esr_brk_comment(esr) == KGDB_COMPILED_DBG_BRK_IMM)
332+
return kgdb_compiled_brk_handler(regs, esr);
314333
}
315334

335+
if (IS_ENABLED(CONFIG_KPROBES)) {
336+
if (esr_brk_comment(esr) == KPROBES_BRK_IMM)
337+
return kprobe_brk_handler(regs, esr);
338+
if (esr_brk_comment(esr) == KPROBES_BRK_SS_IMM)
339+
return kprobe_ss_brk_handler(regs, esr);
340+
}
341+
342+
if (IS_ENABLED(CONFIG_KRETPROBES) &&
343+
esr_brk_comment(esr) == KRETPROBES_BRK_IMM)
344+
return kretprobe_brk_handler(regs, esr);
345+
316346
return DBG_HOOK_ERROR;
317347
}
318348
NOKPROBE_SYMBOL(call_break_hook);

arch/arm64/kernel/kgdb.c

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -234,21 +234,21 @@ int kgdb_arch_handle_exception(int exception_vector, int signo,
234234
return err;
235235
}
236236

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)
238238
{
239239
kgdb_handle_exception(1, SIGTRAP, 0, regs);
240240
return DBG_HOOK_HANDLED;
241241
}
242-
NOKPROBE_SYMBOL(kgdb_brk_fn)
242+
NOKPROBE_SYMBOL(kgdb_brk_handler)
243243

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)
245245
{
246246
compiled_break = 1;
247247
kgdb_handle_exception(1, SIGTRAP, 0, regs);
248248

249249
return DBG_HOOK_HANDLED;
250250
}
251-
NOKPROBE_SYMBOL(kgdb_compiled_brk_fn);
251+
NOKPROBE_SYMBOL(kgdb_compiled_brk_handler);
252252

253253
static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned long esr)
254254
{
@@ -260,16 +260,6 @@ static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned long esr)
260260
}
261261
NOKPROBE_SYMBOL(kgdb_step_brk_fn);
262262

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-
273263
static struct step_hook kgdb_step_hook = {
274264
.fn = kgdb_step_brk_fn
275265
};
@@ -316,8 +306,6 @@ int kgdb_arch_init(void)
316306
if (ret != 0)
317307
return ret;
318308

319-
register_kernel_break_hook(&kgdb_brkpt_hook);
320-
register_kernel_break_hook(&kgdb_compiled_brkpt_hook);
321309
register_kernel_step_hook(&kgdb_step_hook);
322310
return 0;
323311
}
@@ -329,8 +317,6 @@ int kgdb_arch_init(void)
329317
*/
330318
void kgdb_arch_exit(void)
331319
{
332-
unregister_kernel_break_hook(&kgdb_brkpt_hook);
333-
unregister_kernel_break_hook(&kgdb_compiled_brkpt_hook);
334320
unregister_kernel_step_hook(&kgdb_step_hook);
335321
unregister_die_notifier(&kgdb_notifier);
336322
}

arch/arm64/kernel/probes/kprobes.c

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,8 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
292292
return 0;
293293
}
294294

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)
297297
{
298298
struct kprobe *p, *cur_kprobe;
299299
struct kprobe_ctlblk *kcb;
@@ -336,13 +336,8 @@ kprobe_breakpoint_handler(struct pt_regs *regs, unsigned long esr)
336336
return DBG_HOOK_HANDLED;
337337
}
338338

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)
346341
{
347342
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
348343
unsigned long addr = instruction_pointer(regs);
@@ -360,13 +355,8 @@ kprobe_breakpoint_ss_handler(struct pt_regs *regs, unsigned long esr)
360355
return DBG_HOOK_ERROR;
361356
}
362357

363-
static struct break_hook kprobes_break_ss_hook = {
364-
.imm = KPROBES_BRK_SS_IMM,
365-
.fn = kprobe_breakpoint_ss_handler,
366-
};
367-
368-
static int __kprobes
369-
kretprobe_breakpoint_handler(struct pt_regs *regs, unsigned long esr)
358+
int __kprobes
359+
kretprobe_brk_handler(struct pt_regs *regs, unsigned long esr)
370360
{
371361
if (regs->pc != (unsigned long)__kretprobe_trampoline)
372362
return DBG_HOOK_ERROR;
@@ -375,11 +365,6 @@ kretprobe_breakpoint_handler(struct pt_regs *regs, unsigned long esr)
375365
return DBG_HOOK_HANDLED;
376366
}
377367

378-
static struct break_hook kretprobes_break_hook = {
379-
.imm = KRETPROBES_BRK_IMM,
380-
.fn = kretprobe_breakpoint_handler,
381-
};
382-
383368
/*
384369
* Provide a blacklist of symbols identifying ranges which cannot be kprobed.
385370
* This blacklist is exposed to userspace via debugfs (kprobes/blacklist).
@@ -422,9 +407,5 @@ int __kprobes arch_trampoline_kprobe(struct kprobe *p)
422407

423408
int __init arch_init_kprobes(void)
424409
{
425-
register_kernel_break_hook(&kprobes_break_hook);
426-
register_kernel_break_hook(&kprobes_break_ss_hook);
427-
register_kernel_break_hook(&kretprobes_break_hook);
428-
429410
return 0;
430411
}

arch/arm64/kernel/probes/kprobes_trampoline.S

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
SYM_CODE_START(__kretprobe_trampoline)
1313
/*
1414
* 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
1616
* be executed from the trampoline.
1717
*/
1818
brk #KRETPROBES_BRK_IMM

arch/arm64/kernel/probes/uprobes.c

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ int arch_uprobe_exception_notify(struct notifier_block *self,
173173
return NOTIFY_DONE;
174174
}
175175

176-
static int uprobe_breakpoint_handler(struct pt_regs *regs,
176+
int uprobe_brk_handler(struct pt_regs *regs,
177177
unsigned long esr)
178178
{
179179
if (uprobe_pre_sstep_notifier(regs))
@@ -194,20 +194,13 @@ static int uprobe_single_step_handler(struct pt_regs *regs,
194194
return DBG_HOOK_ERROR;
195195
}
196196

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-
203197
/* uprobe single step handler hook */
204198
static struct step_hook uprobes_step_hook = {
205199
.fn = uprobe_single_step_handler,
206200
};
207201

208202
static int __init arch_init_uprobes(void)
209203
{
210-
register_user_break_hook(&uprobes_break_hook);
211204
register_user_step_hook(&uprobes_step_hook);
212205

213206
return 0;

0 commit comments

Comments
 (0)