Commit 4f7ba68e authored by Benjamin Tissoires's avatar Benjamin Tissoires Committed by Pu Lehui
Browse files

bpf: replace bpf_timer_cancel_and_free with a generic helper

mainline inclusion
from mainline-v6.10-rc1
commit fc22d9495f0b32d75b5d25a17b300b7aad05c55d
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/IAGEM1
CVE: CVE-2024-41045

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fc22d9495f0b



--------------------------------

Same reason than most bpf_timer* functions, we need almost the same for
workqueues.
So extract the generic part out of it so bpf_wq_cancel_and_free can reuse
it.

Signed-off-by: default avatarBenjamin Tissoires <bentiss@kernel.org>
Link: https://lore.kernel.org/r/20240420-bpf_wq-v2-4-6c986a5a741f@kernel.org


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarPu Lehui <pulehui@huawei.com>
parent 830f1fc0
Loading
Loading
Loading
Loading
+25 −17
Original line number Diff line number Diff line
@@ -1441,36 +1441,44 @@ static const struct bpf_func_proto bpf_timer_cancel_proto = {
	.arg1_type	= ARG_PTR_TO_TIMER,
};

/* This function is called by map_delete/update_elem for individual element and
 * by ops->map_release_uref when the user space reference to a map reaches zero.
 */
void bpf_timer_cancel_and_free(void *val)
static struct bpf_async_cb *__bpf_async_cancel_and_free(struct bpf_async_kern *async)
{
	struct bpf_async_kern *timer = val;
	struct bpf_hrtimer *t;
	struct bpf_async_cb *cb;

	/* Performance optimization: read timer->timer without lock first. */
	if (!READ_ONCE(timer->timer))
		return;
	/* Performance optimization: read async->cb without lock first. */
	if (!READ_ONCE(async->cb))
		return NULL;

	__bpf_spin_lock_irqsave(&timer->lock);
	__bpf_spin_lock_irqsave(&async->lock);
	/* re-read it under lock */
	t = timer->timer;
	if (!t)
	cb = async->cb;
	if (!cb)
		goto out;
	drop_prog_refcnt(&t->cb);
	drop_prog_refcnt(cb);
	/* The subsequent bpf_timer_start/cancel() helpers won't be able to use
	 * this timer, since it won't be initialized.
	 */
	WRITE_ONCE(timer->timer, NULL);
	WRITE_ONCE(async->cb, NULL);
out:
	__bpf_spin_unlock_irqrestore(&timer->lock);
	__bpf_spin_unlock_irqrestore(&async->lock);
	return cb;
}

/* This function is called by map_delete/update_elem for individual element and
 * by ops->map_release_uref when the user space reference to a map reaches zero.
 */
void bpf_timer_cancel_and_free(void *val)
{
	struct bpf_hrtimer *t;

	t = (struct bpf_hrtimer *)__bpf_async_cancel_and_free(val);

	if (!t)
		return;
	/* Cancel the timer and wait for callback to complete if it was running.
	 * If hrtimer_cancel() can be safely called it's safe to call kfree(t)
	 * right after for both preallocated and non-preallocated maps.
	 * The timer->timer = NULL was already done and no code path can
	 * The async->cb = NULL was already done and no code path can
	 * see address 't' anymore.
	 *
	 * Check that bpf_map_delete/update_elem() wasn't called from timer
@@ -1479,7 +1487,7 @@ void bpf_timer_cancel_and_free(void *val)
	 * return -1). Though callback_fn is still running on this cpu it's
	 * safe to do kfree(t) because bpf_timer_cb() read everything it needed
	 * from 't'. The bpf subprog callback_fn won't be able to access 't',
	 * since timer->timer = NULL was already done. The timer will be
	 * since async->cb = NULL was already done. The timer will be
	 * effectively cancelled because bpf_timer_cb() will return
	 * HRTIMER_NORESTART.
	 */