Skip to content

Commit 7b55edb

Browse files
chenyuan0001gregkh
authored andcommitted
tracing: Fix race condition in kprobe initialization causing NULL pointer dereference
[ Upstream commit 9cf9aa7 ] There is a critical race condition in kprobe initialization that can lead to NULL pointer dereference and kernel crash. [1135630.084782] Unable to handle kernel paging request at virtual address 0000710a04630000 ... [1135630.260314] pstate: 404003c9 (nZcv DAIF +PAN -UAO) [1135630.269239] pc : kprobe_perf_func+0x30/0x260 [1135630.277643] lr : kprobe_dispatcher+0x44/0x60 [1135630.286041] sp : ffffaeff4977fa40 [1135630.293441] x29: ffffaeff4977fa40 x28: ffffaf015340e400 [1135630.302837] x27: 0000000000000000 x26: 0000000000000000 [1135630.312257] x25: ffffaf029ed108a8 x24: ffffaf015340e528 [1135630.321705] x23: ffffaeff4977fc50 x22: ffffaeff4977fc50 [1135630.331154] x21: 0000000000000000 x20: ffffaeff4977fc50 [1135630.340586] x19: ffffaf015340e400 x18: 0000000000000000 [1135630.349985] x17: 0000000000000000 x16: 0000000000000000 [1135630.359285] x15: 0000000000000000 x14: 0000000000000000 [1135630.368445] x13: 0000000000000000 x12: 0000000000000000 [1135630.377473] x11: 0000000000000000 x10: 0000000000000000 [1135630.386411] x9 : 0000000000000000 x8 : 0000000000000000 [1135630.395252] x7 : 0000000000000000 x6 : 0000000000000000 [1135630.403963] x5 : 0000000000000000 x4 : 0000000000000000 [1135630.412545] x3 : 0000710a04630000 x2 : 0000000000000006 [1135630.421021] x1 : ffffaeff4977fc50 x0 : 0000710a04630000 [1135630.429410] Call trace: [1135630.434828] kprobe_perf_func+0x30/0x260 [1135630.441661] kprobe_dispatcher+0x44/0x60 [1135630.448396] aggr_pre_handler+0x70/0xc8 [1135630.454959] kprobe_breakpoint_handler+0x140/0x1e0 [1135630.462435] brk_handler+0xbc/0xd8 [1135630.468437] do_debug_exception+0x84/0x138 [1135630.475074] el1_dbg+0x18/0x8c [1135630.480582] security_file_permission+0x0/0xd0 [1135630.487426] vfs_write+0x70/0x1c0 [1135630.493059] ksys_write+0x5c/0xc8 [1135630.498638] __arm64_sys_write+0x24/0x30 [1135630.504821] el0_svc_common+0x78/0x130 [1135630.510838] el0_svc_handler+0x38/0x78 [1135630.516834] el0_svc+0x8/0x1b0 kernel/trace/trace_kprobe.c: 1308 0xffff3df8995039ec <kprobe_perf_func+0x2c>: ldr x21, [x24,torvalds#120] include/linux/compiler.h: 294 0xffff3df8995039f0 <kprobe_perf_func+0x30>: ldr x1, [x21,x0] kernel/trace/trace_kprobe.c 1308: head = this_cpu_ptr(call->perf_events); 1309: if (hlist_empty(head)) 1310: return 0; crash> struct trace_event_call -o struct trace_event_call { ... [120] struct hlist_head *perf_events; //(call->perf_event) ... } crash> struct trace_event_call ffffaf015340e528 struct trace_event_call { ... perf_events = 0xffff0ad5fa89f088, //this value is correct, but x21 = 0 ... } Race Condition Analysis: The race occurs between kprobe activation and perf_events initialization: CPU0 CPU1 ==== ==== perf_kprobe_init perf_trace_event_init tp_event->perf_events = list;(1) tp_event->class->reg (2)← KPROBE ACTIVE Debug exception triggers ... kprobe_dispatcher kprobe_perf_func (tk->tp.flags & TP_FLAG_PROFILE) head = this_cpu_ptr(call->perf_events)(3) (perf_events is still NULL) Problem: 1. CPU0 executes (1) assigning tp_event->perf_events = list 2. CPU0 executes (2) enabling kprobe functionality via class->reg() 3. CPU1 triggers and reaches kprobe_dispatcher 4. CPU1 checks TP_FLAG_PROFILE - condition passes (step 2 completed) 5. CPU1 calls kprobe_perf_func() and crashes at (3) because call->perf_events is still NULL CPU1 sees that kprobe functionality is enabled but does not see that perf_events has been assigned. Add pairing read and write memory barriers to guarantee that if CPU1 sees that kprobe functionality is enabled, it must also see that perf_events has been assigned. Link: https://lore.kernel.org/all/[email protected]/ Fixes: 50d7805 ("tracing/kprobes: Add probe handler dispatcher to support perf and ftrace concurrent use") Cc: [email protected] Signed-off-by: Yuan Chen <[email protected]> Signed-off-by: Masami Hiramatsu (Google) <[email protected]> [ Adjust context ] Signed-off-by: Sasha Levin <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0ff7912 commit 7b55edb

File tree

4 files changed

+29
-14
lines changed

4 files changed

+29
-14
lines changed

kernel/trace/trace_fprobe.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -342,12 +342,14 @@ static int fentry_dispatcher(struct fprobe *fp, unsigned long entry_ip,
342342
void *entry_data)
343343
{
344344
struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
345+
unsigned int flags = trace_probe_load_flag(&tf->tp);
345346
int ret = 0;
346347

347-
if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE))
348+
if (flags & TP_FLAG_TRACE)
348349
fentry_trace_func(tf, entry_ip, regs);
350+
349351
#ifdef CONFIG_PERF_EVENTS
350-
if (trace_probe_test_flag(&tf->tp, TP_FLAG_PROFILE))
352+
if (flags & TP_FLAG_PROFILE)
351353
ret = fentry_perf_func(tf, entry_ip, regs);
352354
#endif
353355
return ret;
@@ -359,11 +361,12 @@ static void fexit_dispatcher(struct fprobe *fp, unsigned long entry_ip,
359361
void *entry_data)
360362
{
361363
struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
364+
unsigned int flags = trace_probe_load_flag(&tf->tp);
362365

363-
if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE))
366+
if (flags & TP_FLAG_TRACE)
364367
fexit_trace_func(tf, entry_ip, ret_ip, regs, entry_data);
365368
#ifdef CONFIG_PERF_EVENTS
366-
if (trace_probe_test_flag(&tf->tp, TP_FLAG_PROFILE))
369+
if (flags & TP_FLAG_PROFILE)
367370
fexit_perf_func(tf, entry_ip, ret_ip, regs, entry_data);
368371
#endif
369372
}

kernel/trace/trace_kprobe.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,14 +1713,15 @@ static int kprobe_register(struct trace_event_call *event,
17131713
static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)
17141714
{
17151715
struct trace_kprobe *tk = container_of(kp, struct trace_kprobe, rp.kp);
1716+
unsigned int flags = trace_probe_load_flag(&tk->tp);
17161717
int ret = 0;
17171718

17181719
raw_cpu_inc(*tk->nhit);
17191720

1720-
if (trace_probe_test_flag(&tk->tp, TP_FLAG_TRACE))
1721+
if (flags & TP_FLAG_TRACE)
17211722
kprobe_trace_func(tk, regs);
17221723
#ifdef CONFIG_PERF_EVENTS
1723-
if (trace_probe_test_flag(&tk->tp, TP_FLAG_PROFILE))
1724+
if (flags & TP_FLAG_PROFILE)
17241725
ret = kprobe_perf_func(tk, regs);
17251726
#endif
17261727
return ret;
@@ -1732,6 +1733,7 @@ kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs)
17321733
{
17331734
struct kretprobe *rp = get_kretprobe(ri);
17341735
struct trace_kprobe *tk;
1736+
unsigned int flags;
17351737

17361738
/*
17371739
* There is a small chance that get_kretprobe(ri) returns NULL when
@@ -1744,10 +1746,11 @@ kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs)
17441746
tk = container_of(rp, struct trace_kprobe, rp);
17451747
raw_cpu_inc(*tk->nhit);
17461748

1747-
if (trace_probe_test_flag(&tk->tp, TP_FLAG_TRACE))
1749+
flags = trace_probe_load_flag(&tk->tp);
1750+
if (flags & TP_FLAG_TRACE)
17481751
kretprobe_trace_func(tk, ri, regs);
17491752
#ifdef CONFIG_PERF_EVENTS
1750-
if (trace_probe_test_flag(&tk->tp, TP_FLAG_PROFILE))
1753+
if (flags & TP_FLAG_PROFILE)
17511754
kretprobe_perf_func(tk, ri, regs);
17521755
#endif
17531756
return 0; /* We don't tweak kernel, so just return 0 */

kernel/trace/trace_probe.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,16 +268,21 @@ struct event_file_link {
268268
struct list_head list;
269269
};
270270

271+
static inline unsigned int trace_probe_load_flag(struct trace_probe *tp)
272+
{
273+
return smp_load_acquire(&tp->event->flags);
274+
}
275+
271276
static inline bool trace_probe_test_flag(struct trace_probe *tp,
272277
unsigned int flag)
273278
{
274-
return !!(tp->event->flags & flag);
279+
return !!(trace_probe_load_flag(tp) & flag);
275280
}
276281

277282
static inline void trace_probe_set_flag(struct trace_probe *tp,
278283
unsigned int flag)
279284
{
280-
tp->event->flags |= flag;
285+
smp_store_release(&tp->event->flags, tp->event->flags | flag);
281286
}
282287

283288
static inline void trace_probe_clear_flag(struct trace_probe *tp,

kernel/trace/trace_uprobe.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,6 +1514,7 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs)
15141514
struct trace_uprobe *tu;
15151515
struct uprobe_dispatch_data udd;
15161516
struct uprobe_cpu_buffer *ucb = NULL;
1517+
unsigned int flags;
15171518
int ret = 0;
15181519

15191520
tu = container_of(con, struct trace_uprobe, consumer);
@@ -1527,11 +1528,12 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs)
15271528
if (WARN_ON_ONCE(!uprobe_cpu_buffer))
15281529
return 0;
15291530

1530-
if (trace_probe_test_flag(&tu->tp, TP_FLAG_TRACE))
1531+
flags = trace_probe_load_flag(&tu->tp);
1532+
if (flags & TP_FLAG_TRACE)
15311533
ret |= uprobe_trace_func(tu, regs, &ucb);
15321534

15331535
#ifdef CONFIG_PERF_EVENTS
1534-
if (trace_probe_test_flag(&tu->tp, TP_FLAG_PROFILE))
1536+
if (flags & TP_FLAG_PROFILE)
15351537
ret |= uprobe_perf_func(tu, regs, &ucb);
15361538
#endif
15371539
uprobe_buffer_put(ucb);
@@ -1544,6 +1546,7 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con,
15441546
struct trace_uprobe *tu;
15451547
struct uprobe_dispatch_data udd;
15461548
struct uprobe_cpu_buffer *ucb = NULL;
1549+
unsigned int flags;
15471550

15481551
tu = container_of(con, struct trace_uprobe, consumer);
15491552

@@ -1555,11 +1558,12 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con,
15551558
if (WARN_ON_ONCE(!uprobe_cpu_buffer))
15561559
return 0;
15571560

1558-
if (trace_probe_test_flag(&tu->tp, TP_FLAG_TRACE))
1561+
flags = trace_probe_load_flag(&tu->tp);
1562+
if (flags & TP_FLAG_TRACE)
15591563
uretprobe_trace_func(tu, func, regs, &ucb);
15601564

15611565
#ifdef CONFIG_PERF_EVENTS
1562-
if (trace_probe_test_flag(&tu->tp, TP_FLAG_PROFILE))
1566+
if (flags & TP_FLAG_PROFILE)
15631567
uretprobe_perf_func(tu, func, regs, &ucb);
15641568
#endif
15651569
uprobe_buffer_put(ucb);

0 commit comments

Comments
 (0)