PT-2026-116749 · Linux · Linux

CVE-2026-98530

·

Published

2026-10-18

·

Updated

2026-10-18

None

No severity ratings or metrics are available. When they are, we'll update the corresponding info on the page.
In the Linux kernel, the following vulnerability has been resolved:
kprobes: Fix permanent hang when flushing the kprobe optimizer
Writing 0 to /proc/sys/debug/kprobes-optimization while a kprobe is jump-optimized never returns. The writer sleeps in D state forever with kprobe sysctl mutex held, so any later read or write of that sysctl hangs as well. For example, with vfs read+9 as an optimizable address in this build:

cd /sys/kernel/tracing

echo 'p:myprobe vfs read+9' >> kprobe events

echo 1 > events/kprobes/myprobe/enable

# wait until /sys/kernel/debug/kprobes/list shows [OPTIMIZED]

echo 0 > /proc/sys/debug/kprobes-optimization

INFO: task sh:246 blocked for more than 10 seconds. Call Trace: schedule+0x1176/0x4f70 schedule+0xdc/0x2c0 schedule timeout+0x17b/0x260 wait for completion+0x173/0x3c0 wait for kprobe optimizer locked+0xbc/0x130 proc kprobes optimization handler+0x156/0x1b0 proc sys call handler+0x324/0x490 vfs write+0x52d/0xfe0 ksys write+0xff/0x200 do syscall 64+0x106/0x630 entry SYSCALL 64 after hwframe+0x77/0x7f ... INFO: task cat:265 is blocked on a mutex likely owned by task sh:246.
wait for kprobe optimizer locked() reinitializes optimizer completion, asks the optimizer thread to flush and sleeps in wait for completion(). The thread drains the (un)optimizing lists, but calls complete() only if completion done() is true, i.e. if the completion is already done, which never happens while someone waits. disarm all kprobes() and kprobe trace self tests init() wait the same way.
Calling complete() unconditionally would not be enough: the waiter drops kprobe mutex while it sleeps, and nothing else serializes the sysctl handler against the debugfs "enabled" file. A second flusher that still finds the lists non-empty, e.g. because a disabled probe is queued for unoptimizing, reinitializes the completion under the first:
sysctl write debugfs "enabled" write unoptimize all kprobes() wait for kprobe optimizer locked() init completion(c) mutex unlock(&kprobe mutex) wait for completion(c) disarm all kprobes() wait for kprobe optimizer locked() init completion(c) // c->wait is reset, the first // waiter is off the queue mutex unlock(&kprobe mutex) wait for completion(c) kprobe optimizer() complete(c) // wakes the debugfs writer only
where c is &optimizer completion. Lining up the two writes during an optimizer pass loses the sysctl writer this way.
Replace the completion with a counter of optimizer passes, bumped at the end of each pass and signalled with wake up var locked(), both under kprobe mutex. A flusher samples the count and waits with wait var event mutex(), which drops kprobe mutex only while sleeping, so a new count means a whole pass ran in the meantime. Nothing is reinitialized, so several flushers can sleep in the wait at once.
Found an issue in the description? Have something to add? Feel free to write us 👾

Related Identifiers

CVE-2026-98530

Affected Products

Linux