Hello,
I am reporting a kernel WARNING discovered via Syzkaller fuzzing of Linux
7.0-rc5, targeting the new IORING_REGISTER_BPF_FILTER subsystem (new in 7.0).
The bug is confirmed on both 7.0-rc5 and 7.0-rc6. It is NOT fixed in rc6.
In rc6, the WARNING appears to have changed from WARN_ON to WARN_ON_ONCE
(fires only once per boot), which may explain why it was initially missed.
REPORTER
--------
Antonius / Blue Dragon Security
https://bluedragonsec.comanto...@bluedragonsec.comAFFECTED VERSIONS
-----------------
Confirmed: Linux 7.0.0-rc5 (QEMU, KASAN+KFENCE build, Syzkaller)
Confirmed: Linux 7.0.0-rc6 (QEMU, PREEMPT(lazy), PROVE_LOCKING build)
Not affected: kernels prior to 7.0 (IORING_REGISTER_BPF_FILTER is new in 7.0)
Status: NOT fixed in rc6
NOTE ON rc6 BEHAVIOR: The WARNING fires only once per boot in rc6
(WARN_ON_ONCE semantics), confirmed by:
- trace hash "0000000000000000" in the dump
- Silent on subsequent runs within same boot session
- Fires again after reboot
Reset via: echo 0 > /sys/kernel/debug/clear_warn_once (then retest)
CRASH OUTPUT — rc6 (7.0.0-rc6, PREEMPT(lazy))
----------------------------------------------
[ 1021.589216] ------------[ cut here ]------------
[ 1021.589240] WARNING: io_uring/io_uring.c:2187
at io_ring_exit_work+0xbea/0xd4b, CPU#0: kworker/u4:1/14
[ 1021.589298] CPU: 0 UID: 0 PID: 14 Comm: kworker/u4:1
Not tainted 7.0.0-rc6 #1 PREEMPT(lazy)
[ 1021.589326] Workqueue: iou_exit io_ring_exit_work
[ 1021.589346] RIP: 0010:io_ring_exit_work+0xbea/0xd4b
[ 1021.589393] RAX: 0000000000000000 RBX: ffff88810e659778
[ 1021.589432] R13: 0000000000000000 R14: ffff888115c64000
R15: dffffc0000000000
[ 1021.589474] Call Trace:
[ 1021.589487] ? check_prev_add+0x333/0xd30 ← lockdep active
[ 1021.589533] ? __pfx_io_tctx_exit_cb+0x10/0x10
[ 1021.589577] process_one_work+0xa16/0x1900
[ 1021.590019] worker_thread+0x5eb/0xe50
[ 1021.590084] kthread+0x366/0x450
[ 1021.590122] ret_from_fork+0x660/0xa80
[ 1021.590200] </TASK>
[ 1021.590280] ---[ end trace 0000000000000000 ]---
CRASH OUTPUT — rc5 (7.0.0-rc5, PREEMPT(lazy), KASAN)
-----------------------------------------------------
WARNING: io_uring/io_uring.c:2187
at io_ring_exit_work+0xf84/0x1290
Workqueue: iou_exit io_ring_exit_work
R14: ffff88800c2e7000
COMPARISON rc5 vs rc6:
- Bug location: IDENTICAL (io_uring.c:2187, same workqueue)
- WARN type: rc5=WARN_ON (fires every time), rc6=WARN_ON_ONCE (once/boot)
- Function size: rc5=0x1290, rc6=0xd4b (refactoring occurred)
- R14 non-null in both (ctx->bpf_filters still set during teardown)
REPRODUCER (3 syscalls, minimized by Syzkaller)
-----------------------------------------------
Requires: root / CAP_SYS_ADMIN
# Step 1: Register BPF filter at task level (fd=-1)
io_uring_register(-1, IORING_REGISTER_BPF_FILTER=0x25, &filter, 1)
# Step 2: Create ring with DEFER_TASKRUN + R_DISABLED
r0 = io_uring_setup(0x1bcf, {flags=IORING_SETUP_R_DISABLED|
IORING_SETUP_SUBMIT_ALL|IORING_SETUP_SINGLE_ISSUER|
IORING_SETUP_DEFER_TASKRUN, ...})
# Step 3: Register restrictions (NULL arg)
io_uring_register(r0, IORING_REGISTER_RESTRICTIONS=0xb, NULL, 2)
# closing r0 triggers io_ring_exit_work → WARNING at line 2187
BPF filter used: cmd_type=1, opcode=0x3d (BPF_JMP|BPF_JSET), flags=3,
2 instructions: [{code=0x02,jt=0x26,jf=0x02,k=0}, {code=0x06,jt=0x5c,jf=0x06,k=5}]
Syzlang repro:
io_uring_register$IORING_REGISTER_BPF_FILTER(0xffffffffffffffff, 0x25,
&(0x7f0000002280)={0x1, 0x0, 0x0, {0x3d, 0x3, 0x2, 0x0, '\x00',
&(0x7f0000002300)=[{0x2, 0x26, 0x2}, {0x6, 0x5c, 0x6, 0x5}]}}, 0x1)
r0 = io_uring_setup(0x1bcf, &(0x7f0000000000)={0x8, 0x1, 0x30c0, 0x0,
0x8000, 0x7, 0xffffffffffffffff, '\x00', ...})
io_uring_register(r0, 0xb, 0x0, 0x2)
C reproducer attached.
REPRODUCE
sudo ./repro_io_ring_exit_work_loop
ANALYSIS
--------
The WARNING at io_uring.c:2187 fires inside io_ring_ctx_free() during ring
teardown via the iou_exit workqueue. Register R14 is non-null (pointing to
a live kernel object) at the WARN site in both rc5 and rc6, indicating that
ctx->bpf_filters is unexpectedly non-NULL when io_ring_ctx_free() asserts
it should be NULL.
Root cause hypothesis: When IORING_REGISTER_BPF_FILTER is called with
fd=-1 (task-level filter registration, new in Linux 7.0), and a ring is
subsequently created with IORING_SETUP_DEFER_TASKRUN, the ring inherits
the task-level BPF filter via io_ctx_restriction_clone()/io_bpf_filter_clone().
During ring teardown in io_ring_ctx_free(), ctx->bpf_filters is not
properly nulled/freed, triggering the assertion.
The exact WARN_ON is in io_ring_ctx_free() at line 2187 — likely
WARN_ON(ctx->bpf_filters) or similar check on the bpf_filters pointer.
The specific cleanup path in io_bpf_filter_clone() / io_bpf_filters_free()
interaction needs review.
CLASSIFICATION
--------------
CWE: CWE-459 (Incomplete Cleanup) — ctx->bpf_filters not cleaned up
properly on ring teardown when filter was inherited from task context
Impact: Memory leak (bpf_filters object leaked per ring close),
assertion violation in io_ring_ctx_free()
Requires: root/CAP_SYS_ADMIN
No memory corruption (KASAN clean, no double-free detected)
DISCOVERY
---------
Found via Syzkaller fuzzing campaign targeting Linux 7.0-rc5 io_uring
BPF filter subsystem (Blue Dragon Security, March 2026).
No matching syzbot entry found for this specific call path
(IORING_REGISTER_BPF_FILTER with fd=-1 + DEFER_TASKRUN).
Reported-by: Antonius <
anto...@bluedragonsec.com>
Blue Dragon Security —
https://bluedragonsec.com