]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob
01559f0aa14520e742180343e3fd9d256e580ae2
[thirdparty/kernel/stable-queue.git] /
1 From 9cf9aa7b0acfde7545c1a1d912576e9bab28dc6f Mon Sep 17 00:00:00 2001
2 From: Yuan Chen <chenyuan@kylinos.cn>
3 Date: Wed, 1 Oct 2025 03:20:25 +0100
4 Subject: tracing: Fix race condition in kprobe initialization causing NULL pointer dereference
5 MIME-Version: 1.0
6 Content-Type: text/plain; charset=UTF-8
7 Content-Transfer-Encoding: 8bit
8
9 From: Yuan Chen <chenyuan@kylinos.cn>
10
11 commit 9cf9aa7b0acfde7545c1a1d912576e9bab28dc6f upstream.
12
13 There is a critical race condition in kprobe initialization that can lead to
14 NULL pointer dereference and kernel crash.
15
16 [1135630.084782] Unable to handle kernel paging request at virtual address 0000710a04630000
17 ...
18 [1135630.260314] pstate: 404003c9 (nZcv DAIF +PAN -UAO)
19 [1135630.269239] pc : kprobe_perf_func+0x30/0x260
20 [1135630.277643] lr : kprobe_dispatcher+0x44/0x60
21 [1135630.286041] sp : ffffaeff4977fa40
22 [1135630.293441] x29: ffffaeff4977fa40 x28: ffffaf015340e400
23 [1135630.302837] x27: 0000000000000000 x26: 0000000000000000
24 [1135630.312257] x25: ffffaf029ed108a8 x24: ffffaf015340e528
25 [1135630.321705] x23: ffffaeff4977fc50 x22: ffffaeff4977fc50
26 [1135630.331154] x21: 0000000000000000 x20: ffffaeff4977fc50
27 [1135630.340586] x19: ffffaf015340e400 x18: 0000000000000000
28 [1135630.349985] x17: 0000000000000000 x16: 0000000000000000
29 [1135630.359285] x15: 0000000000000000 x14: 0000000000000000
30 [1135630.368445] x13: 0000000000000000 x12: 0000000000000000
31 [1135630.377473] x11: 0000000000000000 x10: 0000000000000000
32 [1135630.386411] x9 : 0000000000000000 x8 : 0000000000000000
33 [1135630.395252] x7 : 0000000000000000 x6 : 0000000000000000
34 [1135630.403963] x5 : 0000000000000000 x4 : 0000000000000000
35 [1135630.412545] x3 : 0000710a04630000 x2 : 0000000000000006
36 [1135630.421021] x1 : ffffaeff4977fc50 x0 : 0000710a04630000
37 [1135630.429410] Call trace:
38 [1135630.434828] kprobe_perf_func+0x30/0x260
39 [1135630.441661] kprobe_dispatcher+0x44/0x60
40 [1135630.448396] aggr_pre_handler+0x70/0xc8
41 [1135630.454959] kprobe_breakpoint_handler+0x140/0x1e0
42 [1135630.462435] brk_handler+0xbc/0xd8
43 [1135630.468437] do_debug_exception+0x84/0x138
44 [1135630.475074] el1_dbg+0x18/0x8c
45 [1135630.480582] security_file_permission+0x0/0xd0
46 [1135630.487426] vfs_write+0x70/0x1c0
47 [1135630.493059] ksys_write+0x5c/0xc8
48 [1135630.498638] __arm64_sys_write+0x24/0x30
49 [1135630.504821] el0_svc_common+0x78/0x130
50 [1135630.510838] el0_svc_handler+0x38/0x78
51 [1135630.516834] el0_svc+0x8/0x1b0
52
53 kernel/trace/trace_kprobe.c: 1308
54 0xffff3df8995039ec <kprobe_perf_func+0x2c>: ldr x21, [x24,#120]
55 include/linux/compiler.h: 294
56 0xffff3df8995039f0 <kprobe_perf_func+0x30>: ldr x1, [x21,x0]
57
58 kernel/trace/trace_kprobe.c
59 1308: head = this_cpu_ptr(call->perf_events);
60 1309: if (hlist_empty(head))
61 1310: return 0;
62
63 crash> struct trace_event_call -o
64 struct trace_event_call {
65 ...
66 [120] struct hlist_head *perf_events; //(call->perf_event)
67 ...
68 }
69
70 crash> struct trace_event_call ffffaf015340e528
71 struct trace_event_call {
72 ...
73 perf_events = 0xffff0ad5fa89f088, //this value is correct, but x21 = 0
74 ...
75 }
76
77 Race Condition Analysis:
78
79 The race occurs between kprobe activation and perf_events initialization:
80
81 CPU0 CPU1
82 ==== ====
83 perf_kprobe_init
84 perf_trace_event_init
85 tp_event->perf_events = list;(1)
86 tp_event->class->reg (2)← KPROBE ACTIVE
87 Debug exception triggers
88 ...
89 kprobe_dispatcher
90 kprobe_perf_func (tk->tp.flags & TP_FLAG_PROFILE)
91 head = this_cpu_ptr(call->perf_events)(3)
92 (perf_events is still NULL)
93
94 Problem:
95 1. CPU0 executes (1) assigning tp_event->perf_events = list
96 2. CPU0 executes (2) enabling kprobe functionality via class->reg()
97 3. CPU1 triggers and reaches kprobe_dispatcher
98 4. CPU1 checks TP_FLAG_PROFILE - condition passes (step 2 completed)
99 5. CPU1 calls kprobe_perf_func() and crashes at (3) because
100 call->perf_events is still NULL
101
102 CPU1 sees that kprobe functionality is enabled but does not see that
103 perf_events has been assigned.
104
105 Add pairing read and write memory barriers to guarantee that if CPU1
106 sees that kprobe functionality is enabled, it must also see that
107 perf_events has been assigned.
108
109 Link: https://lore.kernel.org/all/20251001022025.44626-1-chenyuan_fl@163.com/
110
111 Fixes: 50d780560785 ("tracing/kprobes: Add probe handler dispatcher to support perf and ftrace concurrent use")
112 Cc: stable@vger.kernel.org
113 Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
114 Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
115 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
116 ---
117 kernel/trace/trace_fprobe.c | 10 ++++++----
118 kernel/trace/trace_kprobe.c | 11 +++++++----
119 kernel/trace/trace_probe.h | 9 +++++++--
120 kernel/trace/trace_uprobe.c | 12 ++++++++----
121 4 files changed, 28 insertions(+), 14 deletions(-)
122
123 --- a/kernel/trace/trace_fprobe.c
124 +++ b/kernel/trace/trace_fprobe.c
125 @@ -522,13 +522,14 @@ static int fentry_dispatcher(struct fpro
126 void *entry_data)
127 {
128 struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
129 + unsigned int flags = trace_probe_load_flag(&tf->tp);
130 int ret = 0;
131
132 - if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE))
133 + if (flags & TP_FLAG_TRACE)
134 fentry_trace_func(tf, entry_ip, fregs);
135
136 #ifdef CONFIG_PERF_EVENTS
137 - if (trace_probe_test_flag(&tf->tp, TP_FLAG_PROFILE))
138 + if (flags & TP_FLAG_PROFILE)
139 ret = fentry_perf_func(tf, entry_ip, fregs);
140 #endif
141 return ret;
142 @@ -540,11 +541,12 @@ static void fexit_dispatcher(struct fpro
143 void *entry_data)
144 {
145 struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
146 + unsigned int flags = trace_probe_load_flag(&tf->tp);
147
148 - if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE))
149 + if (flags & TP_FLAG_TRACE)
150 fexit_trace_func(tf, entry_ip, ret_ip, fregs, entry_data);
151 #ifdef CONFIG_PERF_EVENTS
152 - if (trace_probe_test_flag(&tf->tp, TP_FLAG_PROFILE))
153 + if (flags & TP_FLAG_PROFILE)
154 fexit_perf_func(tf, entry_ip, ret_ip, fregs, entry_data);
155 #endif
156 }
157 --- a/kernel/trace/trace_kprobe.c
158 +++ b/kernel/trace/trace_kprobe.c
159 @@ -1815,14 +1815,15 @@ static int kprobe_register(struct trace_
160 static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)
161 {
162 struct trace_kprobe *tk = container_of(kp, struct trace_kprobe, rp.kp);
163 + unsigned int flags = trace_probe_load_flag(&tk->tp);
164 int ret = 0;
165
166 raw_cpu_inc(*tk->nhit);
167
168 - if (trace_probe_test_flag(&tk->tp, TP_FLAG_TRACE))
169 + if (flags & TP_FLAG_TRACE)
170 kprobe_trace_func(tk, regs);
171 #ifdef CONFIG_PERF_EVENTS
172 - if (trace_probe_test_flag(&tk->tp, TP_FLAG_PROFILE))
173 + if (flags & TP_FLAG_PROFILE)
174 ret = kprobe_perf_func(tk, regs);
175 #endif
176 return ret;
177 @@ -1834,6 +1835,7 @@ kretprobe_dispatcher(struct kretprobe_in
178 {
179 struct kretprobe *rp = get_kretprobe(ri);
180 struct trace_kprobe *tk;
181 + unsigned int flags;
182
183 /*
184 * There is a small chance that get_kretprobe(ri) returns NULL when
185 @@ -1846,10 +1848,11 @@ kretprobe_dispatcher(struct kretprobe_in
186 tk = container_of(rp, struct trace_kprobe, rp);
187 raw_cpu_inc(*tk->nhit);
188
189 - if (trace_probe_test_flag(&tk->tp, TP_FLAG_TRACE))
190 + flags = trace_probe_load_flag(&tk->tp);
191 + if (flags & TP_FLAG_TRACE)
192 kretprobe_trace_func(tk, ri, regs);
193 #ifdef CONFIG_PERF_EVENTS
194 - if (trace_probe_test_flag(&tk->tp, TP_FLAG_PROFILE))
195 + if (flags & TP_FLAG_PROFILE)
196 kretprobe_perf_func(tk, ri, regs);
197 #endif
198 return 0; /* We don't tweak kernel, so just return 0 */
199 --- a/kernel/trace/trace_probe.h
200 +++ b/kernel/trace/trace_probe.h
201 @@ -271,16 +271,21 @@ struct event_file_link {
202 struct list_head list;
203 };
204
205 +static inline unsigned int trace_probe_load_flag(struct trace_probe *tp)
206 +{
207 + return smp_load_acquire(&tp->event->flags);
208 +}
209 +
210 static inline bool trace_probe_test_flag(struct trace_probe *tp,
211 unsigned int flag)
212 {
213 - return !!(tp->event->flags & flag);
214 + return !!(trace_probe_load_flag(tp) & flag);
215 }
216
217 static inline void trace_probe_set_flag(struct trace_probe *tp,
218 unsigned int flag)
219 {
220 - tp->event->flags |= flag;
221 + smp_store_release(&tp->event->flags, tp->event->flags | flag);
222 }
223
224 static inline void trace_probe_clear_flag(struct trace_probe *tp,
225 --- a/kernel/trace/trace_uprobe.c
226 +++ b/kernel/trace/trace_uprobe.c
227 @@ -1547,6 +1547,7 @@ static int uprobe_dispatcher(struct upro
228 struct trace_uprobe *tu;
229 struct uprobe_dispatch_data udd;
230 struct uprobe_cpu_buffer *ucb = NULL;
231 + unsigned int flags;
232 int ret = 0;
233
234 tu = container_of(con, struct trace_uprobe, consumer);
235 @@ -1561,11 +1562,12 @@ static int uprobe_dispatcher(struct upro
236 if (WARN_ON_ONCE(!uprobe_cpu_buffer))
237 return 0;
238
239 - if (trace_probe_test_flag(&tu->tp, TP_FLAG_TRACE))
240 + flags = trace_probe_load_flag(&tu->tp);
241 + if (flags & TP_FLAG_TRACE)
242 ret |= uprobe_trace_func(tu, regs, &ucb);
243
244 #ifdef CONFIG_PERF_EVENTS
245 - if (trace_probe_test_flag(&tu->tp, TP_FLAG_PROFILE))
246 + if (flags & TP_FLAG_PROFILE)
247 ret |= uprobe_perf_func(tu, regs, &ucb);
248 #endif
249 uprobe_buffer_put(ucb);
250 @@ -1579,6 +1581,7 @@ static int uretprobe_dispatcher(struct u
251 struct trace_uprobe *tu;
252 struct uprobe_dispatch_data udd;
253 struct uprobe_cpu_buffer *ucb = NULL;
254 + unsigned int flags;
255
256 tu = container_of(con, struct trace_uprobe, consumer);
257
258 @@ -1590,11 +1593,12 @@ static int uretprobe_dispatcher(struct u
259 if (WARN_ON_ONCE(!uprobe_cpu_buffer))
260 return 0;
261
262 - if (trace_probe_test_flag(&tu->tp, TP_FLAG_TRACE))
263 + flags = trace_probe_load_flag(&tu->tp);
264 + if (flags & TP_FLAG_TRACE)
265 uretprobe_trace_func(tu, func, regs, &ucb);
266
267 #ifdef CONFIG_PERF_EVENTS
268 - if (trace_probe_test_flag(&tu->tp, TP_FLAG_PROFILE))
269 + if (flags & TP_FLAG_PROFILE)
270 uretprobe_perf_func(tu, func, regs, &ucb);
271 #endif
272 uprobe_buffer_put(ucb);