mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: shaos <shawnq1@163.com>
To: Amery Hung <amery.hung@bytedance.com>
Cc: "Jamal Hadi Salim" <jhs@mojatatu.com>,
	"Jiri Pirko" <jiri@resnulli.us>,
	"Martin KaFai Lau" <martin.lau@kernel.org>,
	"Toke Høiland-Jørgensen" <toke@redhat.com>,
	netdev@vger.kernel.org, bpf@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH] bpf: net_sched: reject bpf_throw() in qdisc teardown callbacks
Date: Wed, 4 Nov 2026 01:32:29 +0800 (CST)	[thread overview]
Message-ID: <6AEA1AAD.2E7977.08739@m16.mail.163.com> (raw)

Hello,

A BPF qdisc's generated reset/destroy epilogue cancels its watchdog
timer.  bpf_throw() unwinds directly to the exception callback and
skips that epilogue.  If the timer was armed, qdisc deletion frees the
qdisc while the embedded timer remains queued.  Later timer-queue
activity accesses the freed qdisc, producing a KASAN slab-use-after-free
and a kernel crash.

The reproducer registers a BPF qdisc whose dequeue callback arms the
watchdog and whose reset/destroy callbacks throw.  Two fresh KASAN boots
reported use-after-free in rb_insert_color on an object allocated by
qdisc_alloc() and later freed by qdisc teardown.  An otherwise identical
no-throw control completed deletion cleanly.  The required BPF and
network-administration capabilities make this a privileged local kernel
crash; no privilege escalation or code execution is demonstrated.

HEAD commit:    c03c1a14507dfaf8b856c2608f9ac216021fc222
Git tree:       30fd2ef82912c26cf3a58ad0ba6afc375fa851ac
Source version: Linux 7.3
Compiler:       gcc 11.4.0, GNU ld 2.38, clang 20 (BPF PoC)

The console log, full KASAN kernel config, and BPF C reproducer are
available from one fixed-revision Gist:

Console output:
https://gist.githubusercontent.com/shaos26/9908a8ddac69e303cb5dcc709958537f/raw/3fa043e43683907bcccacdc80a71b150920049da/console-output.txt
Kernel config:
https://gist.githubusercontent.com/shaos26/9908a8ddac69e303cb5dcc709958537f/raw/3fa043e43683907bcccacdc80a71b150920049da/kernel-config.txt
PoC source:
https://gist.githubusercontent.com/shaos26/9908a8ddac69e303cb5dcc709958537f/raw/3fa043e43683907bcccacdc80a71b150920049da/poc.c

Run this as root in a disposable VM booted with the target kernel,
CONFIG_DEBUG_INFO_BTF=y, bpftool, iproute2, clang 20, and libbpf-dev.
Build each variant against that kernel's BTF; test them on separate
fresh boots.  With libbpf headers installed under /usr/include:

    bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
    CLANG=/usr/lib/llvm-20/bin/clang
    LIBBPF_INC=/usr/include
    "$CLANG" -target bpf -D__TARGET_ARCH_x86 -O2 -g -Wall -Werror \
      -Wno-missing-declarations -I. -I"$LIBBPF_INC" \
      -DVARIANT_THROW=1 -c poc.c -o qdisc-throw.bpf.o
    "$CLANG" -target bpf -D__TARGET_ARCH_x86 -O2 -g -Wall -Werror \
      -Wno-missing-declarations -I. -I"$LIBBPF_INC" \
      -DVARIANT_THROW=0 -c poc.c -o qdisc-control.bpf.o

Mount bpffs if it is not already mounted, register the matching object,
attach its qdisc to loopback, send one packet to arm the watchdog, and
delete the qdisc:

    mkdir -p /sys/fs/bpf
    mount -t bpf bpffs /sys/fs/bpf 2>/dev/null || true
    bpftool struct_ops register qdisc-throw.bpf.o /sys/fs/bpf
    ip link set lo up
    tc qdisc add dev lo root handle 1: agent31_uaf
    ping -c 1 -W 1 127.0.0.1
    tc qdisc del dev lo root
    sleep 10

For the separate control boot, register qdisc-control.bpf.o and use
qdisc kind agent31_ctl in the same sequence.

On the vulnerable kernel, the two throw runs reproduced the KASAN UAF;
the no-throw control survived.  With the patch below, the verifier
rejects bpf_throw() from reset/destroy with -EACCES, before qdisc
registration.  The patched control object registered, attached, armed
and deleted normally, then survived the watchdog interval.

Fixes: 7a2dafda950b ("bpf: net_sched: Add a qdisc watchdog timer")
Signed-off-by: shaos <shawnq1@163.com>
---
 net/sched/bpf_qdisc.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/net/sched/bpf_qdisc.c b/net/sched/bpf_qdisc.c
index 098ca02aed89c..61f0dc93d3856 100644
--- a/net/sched/bpf_qdisc.c
+++ b/net/sched/bpf_qdisc.c
@@ -12,6 +12,7 @@
 #define QDISC_MOFF_IDX(moff)	(moff / sizeof(void (*)(void)))
 
 static struct bpf_struct_ops bpf_Qdisc_ops;
+static const struct bpf_verifier_ops bpf_qdisc_verifier_ops;
 
 struct bpf_sched_data {
 	struct qdisc_watchdog watchdog;
@@ -131,6 +132,7 @@ static int bpf_qdisc_btf_struct_access(struct bpf_verifier_log *log,
 }
 
 BTF_ID_LIST_SINGLE(bpf_qdisc_init_prologue_ids, func, bpf_qdisc_init_prologue)
+BTF_ID_LIST_SINGLE(bpf_qdisc_throw_ids, func, bpf_throw)
 
 static int bpf_qdisc_gen_prologue(struct bpf_insn *insn_buf, bool direct_write,
 				  const struct bpf_prog *prog)
@@ -307,20 +309,31 @@ enum qdisc_ops_kf_flags {
 	QDISC_OPS_KF_COMMON		= 0,
 	QDISC_OPS_KF_ENQUEUE		= 1 << 0,
 	QDISC_OPS_KF_DEQUEUE		= 1 << 1,
+	QDISC_OPS_KF_TEARDOWN		= 1 << 2,
 };
 
 static const u32 qdisc_ops_context_flags[] = {
 	[QDISC_OP_IDX(enqueue)]		= QDISC_OPS_KF_ENQUEUE,
 	[QDISC_OP_IDX(dequeue)]		= QDISC_OPS_KF_DEQUEUE,
 	[QDISC_OP_IDX(init)]		= QDISC_OPS_KF_COMMON,
-	[QDISC_OP_IDX(reset)]		= QDISC_OPS_KF_COMMON,
-	[QDISC_OP_IDX(destroy)]		= QDISC_OPS_KF_COMMON,
+	[QDISC_OP_IDX(reset)]		= QDISC_OPS_KF_TEARDOWN,
+	[QDISC_OP_IDX(destroy)]		= QDISC_OPS_KF_TEARDOWN,
 };
 
 static int bpf_qdisc_kfunc_filter(const struct bpf_prog *prog, u32 kfunc_id)
 {
 	u32 moff, flags;
 
+	/* bpf_throw() bypasses the generated watchdog-cancellation epilogue. */
+	if (kfunc_id == bpf_qdisc_throw_ids[0] &&
+	    prog->aux->st_ops &&
+	    prog->aux->st_ops->verifier_ops == &bpf_qdisc_verifier_ops) {
+		moff = prog->aux->attach_st_ops_member_off;
+		flags = qdisc_ops_context_flags[QDISC_MOFF_IDX(moff)];
+		if (flags & QDISC_OPS_KF_TEARDOWN)
+			return -EACCES;
+	}
+
 	if (!btf_id_set8_contains(&qdisc_kfunc_ids, kfunc_id))
 		return 0;
 
-- 
2.34.1


             reply	other threads:[~2026-11-03 17:33 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03 17:32 shaos [this message]
2026-11-03 18:18 ` Alexei Starovoitov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6AEA1AAD.2E7977.08739@m16.mail.163.com \
    --to=shawnq1@163.com \
    --cc=amery.hung@bytedance.com \
    --cc=bpf@vger.kernel.org \
    --cc=jhs@mojatatu.com \
    --cc=jiri@resnulli.us \
    --cc=linux-kernel@vger.kernel.org \
    --cc=martin.lau@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=toke@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®