On Tue, Dec 31, 2024 at 03:35:09AM +0000, Pu Lehui wrote: > From: Pu Lehui <pulehui@xxxxxxxxxx> > > Commit ef1b808e3b7c ("bpf: Fix UAF via mismatching bpf_prog/attachment > RCU flavors") resolved a possible UAF issue in uprobes that attach > non-sleepable bpf prog by explicitly waiting for a tasks-trace-RCU grace > period. But, in the current implementation, synchronize_rcu_tasks_trace > is included within the mutex critical section, which increases the > length of the critical section and may affect performance. So let's move > out synchronize_rcu_tasks_trace from mutex CS. > > Signed-off-by: Pu Lehui <pulehui@xxxxxxxxxx> > --- > kernel/trace/bpf_trace.c | 18 +++++++++++------- > 1 file changed, 11 insertions(+), 7 deletions(-) > > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c > index 48db147c6c7d..30ef8a6f5ca2 100644 > --- a/kernel/trace/bpf_trace.c > +++ b/kernel/trace/bpf_trace.c > @@ -2245,12 +2245,15 @@ void perf_event_detach_bpf_prog(struct perf_event *event) > { > struct bpf_prog_array *old_array; > struct bpf_prog_array *new_array; > + struct bpf_prog *prog; > int ret; > > mutex_lock(&bpf_event_mutex); > > - if (!event->prog) > - goto unlock; > + if (!event->prog) { > + mutex_unlock(&bpf_event_mutex); > + return; > + } > > old_array = bpf_event_rcu_dereference(event->tp_event->prog_array); > if (!old_array) > @@ -2265,6 +2268,11 @@ void perf_event_detach_bpf_prog(struct perf_event *event) > } > > put: > + prog = event->prog; > + event->prog = NULL; > + > + mutex_unlock(&bpf_event_mutex); > + > /* > * It could be that the bpf_prog is not sleepable (and will be freed > * via normal RCU), but is called from a point that supports sleepable > @@ -2272,11 +2280,7 @@ void perf_event_detach_bpf_prog(struct perf_event *event) > */ > synchronize_rcu_tasks_trace(); > > - bpf_prog_put(event->prog); > - event->prog = NULL; > - > -unlock: > - mutex_unlock(&bpf_event_mutex); > + bpf_prog_put(prog); > } > > int perf_event_query_prog_array(struct perf_event *event, void __user *info) > -- > 2.34.1 > would something like below be simpler? (not tested) jirka --- diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 973104f861e9..a4c0efa3a26e 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2246,6 +2246,7 @@ void perf_event_detach_bpf_prog(struct perf_event *event) { struct bpf_prog_array *old_array; struct bpf_prog_array *new_array; + struct bpf_prog *prog = NULL; int ret; mutex_lock(&bpf_event_mutex); @@ -2266,18 +2267,22 @@ void perf_event_detach_bpf_prog(struct perf_event *event) } put: - /* - * It could be that the bpf_prog is not sleepable (and will be freed - * via normal RCU), but is called from a point that supports sleepable - * programs and uses tasks-trace-RCU. - */ - synchronize_rcu_tasks_trace(); - - bpf_prog_put(event->prog); + prog = event->prog; event->prog = NULL; unlock: mutex_unlock(&bpf_event_mutex); + + if (prog) { + /* + * It could be that the bpf_prog is not sleepable (and will be freed + * via normal RCU), but is called from a point that supports sleepable + * programs and uses tasks-trace-RCU. + */ + synchronize_rcu_tasks_trace(); + + bpf_prog_put(prog); + } } int perf_event_query_prog_array(struct perf_event *event, void __user *info)