Skip to content

Commit 0281b91

Browse files
Martin KaFai Lauborkmann
authored andcommitted
bpf: Fix racing between bpf_timer_cancel_and_free and bpf_timer_cancel
The following race is possible between bpf_timer_cancel_and_free and bpf_timer_cancel. It will lead a UAF on the timer->timer. bpf_timer_cancel(); spin_lock(); t = timer->time; spin_unlock(); bpf_timer_cancel_and_free(); spin_lock(); t = timer->timer; timer->timer = NULL; spin_unlock(); hrtimer_cancel(&t->timer); kfree(t); /* UAF on t */ hrtimer_cancel(&t->timer); In bpf_timer_cancel_and_free, this patch frees the timer->timer after a rcu grace period. This requires a rcu_head addition to the "struct bpf_hrtimer". Another kfree(t) happens in bpf_timer_init, this does not need a kfree_rcu because it is still under the spin_lock and timer->timer has not been visible by others yet. In bpf_timer_cancel, rcu_read_lock() is added because this helper can be used in a non rcu critical section context (e.g. from a sleepable bpf prog). Other timer->timer usages in helpers.c have been audited, bpf_timer_cancel() is the only place where timer->timer is used outside of the spin_lock. Another solution considered is to mark a t->flag in bpf_timer_cancel and clear it after hrtimer_cancel() is done. In bpf_timer_cancel_and_free, it busy waits for the flag to be cleared before kfree(t). This patch goes with a straight forward solution and frees timer->timer after a rcu grace period. Fixes: b00628b ("bpf: Introduce bpf timers.") Suggested-by: Alexei Starovoitov <[email protected]> Signed-off-by: Martin KaFai Lau <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Acked-by: Hou Tao <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 54d46c9 commit 0281b91

File tree

1 file changed

+4
-1
lines changed

1 file changed

+4
-1
lines changed

kernel/bpf/helpers.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,7 @@ struct bpf_hrtimer {
11011101
struct bpf_prog *prog;
11021102
void __rcu *callback_fn;
11031103
void *value;
1104+
struct rcu_head rcu;
11041105
};
11051106

11061107
/* the actual struct hidden inside uapi struct bpf_timer */
@@ -1332,6 +1333,7 @@ BPF_CALL_1(bpf_timer_cancel, struct bpf_timer_kern *, timer)
13321333

13331334
if (in_nmi())
13341335
return -EOPNOTSUPP;
1336+
rcu_read_lock();
13351337
__bpf_spin_lock_irqsave(&timer->lock);
13361338
t = timer->timer;
13371339
if (!t) {
@@ -1353,6 +1355,7 @@ BPF_CALL_1(bpf_timer_cancel, struct bpf_timer_kern *, timer)
13531355
* if it was running.
13541356
*/
13551357
ret = ret ?: hrtimer_cancel(&t->timer);
1358+
rcu_read_unlock();
13561359
return ret;
13571360
}
13581361

@@ -1407,7 +1410,7 @@ void bpf_timer_cancel_and_free(void *val)
14071410
*/
14081411
if (this_cpu_read(hrtimer_running) != t)
14091412
hrtimer_cancel(&t->timer);
1410-
kfree(t);
1413+
kfree_rcu(t, rcu);
14111414
}
14121415

14131416
BPF_CALL_2(bpf_kptr_xchg, void *, map_value, void *, ptr)

0 commit comments

Comments
 (0)