CVE-2026-46196
Description
In the Linux kernel, the following vulnerability has been resolved:
tracepoint: balance regfunc() on func_add() failure in tracepoint_add_func()
When a tracepoint goes through the 0 -> 1 transition, tracepoint_add_func() invokes the subsystem's ext->regfunc() before attempting to install the new probe via func_add(). If func_add() then fails (for example, when allocate_probes() cannot allocate a new probe array under memory pressure and returns -ENOMEM), the function returns the error without calling the matching ext->unregfunc(), leaving the side effects of regfunc() behind with no installed probe to justify them.
For syscall tracepoints this is particularly unpleasant: syscall_regfunc() bumps sys_tracepoint_refcount and sets SYSCALL_TRACEPOINT on every task. After a leaked failure, the refcount is stuck at a non-zero value with no consumer, and every task continues paying the syscall trace entry/exit overhead until reboot. Other subsystems providing regfunc()/unregfunc() pairs exhibit similarly scoped persistent state.
Mirror the existing 1 -> 0 cleanup and call ext->unregfunc() in the func_add() error path, gated on the same condition used there so the unwind is symmetric with the registration.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A missing unregfunc() call in Linux kernel tracepoint registration can leave persistent syscall tracing overhead after a memory allocation failure.
Vulnerability
The vulnerability resides in the tracepoint_add_func() function in the Linux kernel. When a tracepoint transitions from zero to one active probe, the function calls the subsystem's ext->regfunc() before attempting to install the new probe via func_add(). If func_add() fails (e.g., allocate_probes() returns -ENOMEM under memory pressure), the error path returns the error without calling the matching ext->unregfunc(), leaving the side effects of regfunc() behind with no installed probe to justify them. This affects all kernel versions prior to the fix commit [1].
Exploitation
An attacker would need to trigger a memory allocation failure in the kernel's probe array allocation, typically by exhausting system memory (e.g., via controlled memory pressure). No special privileges are required beyond the ability to cause memory exhaustion. The sequence involves activating a tracepoint (e.g., via perf or ftrace) and then inducing memory pressure so that allocate_probes() fails. The regfunc() side effects persist indefinitely.
Impact
For syscall tracepoints, syscall_regfunc() increments sys_tracepoint_refcount and sets the SYSCALL_TRACEPOINT flag on every task. After a leaked failure, the refcount remains non-zero with no consumer, causing every task to incur syscall trace entry/exit overhead until the system is rebooted. Other subsystems providing regfunc()/unregfunc() pairs exhibit similarly scoped persistent state. This constitutes a denial-of-service vulnerability through performance degradation.
Mitigation
The fix is commit 2c5b8eeea006 in the Linux kernel stable tree [1]. It adds a call to ext->unregfunc() in the func_add() error path, gated on the same condition used during registration, ensuring symmetric cleanup. Users should apply the kernel update. No workaround is available; a reboot is required to clear the leaked state if the bug has been triggered.
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
10247ed8a969f9tracepoint: balance regfunc() on func_add() failure in tracepoint_add_func()
1 file changed · +2 −1
kernel/tracepoint.c+2 −1 modifieddiff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 8d1507dd072469..f7a4210d5d5e2a 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -337,6 +337,8 @@ static int tracepoint_add_func(struct tracepoint *tp, lockdep_is_held(&tracepoints_mutex)); old = func_add(&tp_funcs, func, prio); if (IS_ERR(old)) { + if (tp->unregfunc && !static_key_enabled(&tp->key)) + tp->unregfunc(); WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); return PTR_ERR(old); } -- cgit 1.3-korg
7bcadb3c2bc1tracepoint: balance regfunc() on func_add() failure in tracepoint_add_func()
1 file changed · +2 −1
kernel/tracepoint.c+2 −1 modifieddiff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 8879da16ef4d69..4cee42ed5fc8d5 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -337,6 +337,8 @@ static int tracepoint_add_func(struct tracepoint *tp, lockdep_is_held(&tracepoints_mutex)); old = func_add(&tp_funcs, func, prio); if (IS_ERR(old)) { + if (tp->unregfunc && !static_key_enabled(&tp->key)) + tp->unregfunc(); WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); return PTR_ERR(old); } -- cgit 1.3-korg
2c5b8eeea006tracepoint: balance regfunc() on func_add() failure in tracepoint_add_func()
1 file changed · +2 −1
kernel/tracepoint.c+2 −1 modifieddiff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 62719d2941c900..1abaeb32a8e1eb 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -291,6 +291,8 @@ static int tracepoint_add_func(struct tracepoint *tp, lockdep_is_held(&tracepoints_mutex)); old = func_add(&tp_funcs, func, prio); if (IS_ERR(old)) { + if (tp->ext && tp->ext->unregfunc && !static_key_enabled(&tp->key)) + tp->ext->unregfunc(); WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); return PTR_ERR(old); } -- cgit 1.3-korg
342829e042actracepoint: balance regfunc() on func_add() failure in tracepoint_add_func()
1 file changed · +2 −1
kernel/tracepoint.c+2 −1 modifieddiff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 91905aa19294d2..dffef52a807bc8 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -300,6 +300,8 @@ static int tracepoint_add_func(struct tracepoint *tp, lockdep_is_held(&tracepoints_mutex)); old = func_add(&tp_funcs, func, prio); if (IS_ERR(old)) { + if (tp->ext && tp->ext->unregfunc && !static_key_enabled(&tp->key)) + tp->ext->unregfunc(); WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); return PTR_ERR(old); } -- cgit 1.3-korg
fad217e16fdetracepoint: balance regfunc() on func_add() failure in tracepoint_add_func()
1 file changed · +2 −1
kernel/tracepoint.c+2 −1 modifieddiff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 91905aa19294d2..dffef52a807bc8 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -300,6 +300,8 @@ static int tracepoint_add_func(struct tracepoint *tp, lockdep_is_held(&tracepoints_mutex)); old = func_add(&tp_funcs, func, prio); if (IS_ERR(old)) { + if (tp->ext && tp->ext->unregfunc && !static_key_enabled(&tp->key)) + tp->ext->unregfunc(); WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); return PTR_ERR(old); } -- cgit 1.3-korg
247ed8a969f9tracepoint: balance regfunc() on func_add() failure in tracepoint_add_func()
1 file changed · +2 −1
kernel/tracepoint.c+2 −1 modifieddiff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 8d1507dd072469..f7a4210d5d5e2a 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -337,6 +337,8 @@ static int tracepoint_add_func(struct tracepoint *tp, lockdep_is_held(&tracepoints_mutex)); old = func_add(&tp_funcs, func, prio); if (IS_ERR(old)) { + if (tp->unregfunc && !static_key_enabled(&tp->key)) + tp->unregfunc(); WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); return PTR_ERR(old); } -- cgit 1.3-korg
7bcadb3c2bc1tracepoint: balance regfunc() on func_add() failure in tracepoint_add_func()
1 file changed · +2 −1
kernel/tracepoint.c+2 −1 modifieddiff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 8879da16ef4d69..4cee42ed5fc8d5 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -337,6 +337,8 @@ static int tracepoint_add_func(struct tracepoint *tp, lockdep_is_held(&tracepoints_mutex)); old = func_add(&tp_funcs, func, prio); if (IS_ERR(old)) { + if (tp->unregfunc && !static_key_enabled(&tp->key)) + tp->unregfunc(); WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); return PTR_ERR(old); } -- cgit 1.3-korg
fad217e16fdetracepoint: balance regfunc() on func_add() failure in tracepoint_add_func()
1 file changed · +2 −1
kernel/tracepoint.c+2 −1 modifieddiff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 91905aa19294d2..dffef52a807bc8 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -300,6 +300,8 @@ static int tracepoint_add_func(struct tracepoint *tp, lockdep_is_held(&tracepoints_mutex)); old = func_add(&tp_funcs, func, prio); if (IS_ERR(old)) { + if (tp->ext && tp->ext->unregfunc && !static_key_enabled(&tp->key)) + tp->ext->unregfunc(); WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); return PTR_ERR(old); } -- cgit 1.3-korg
2c5b8eeea006tracepoint: balance regfunc() on func_add() failure in tracepoint_add_func()
1 file changed · +2 −1
kernel/tracepoint.c+2 −1 modifieddiff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 62719d2941c900..1abaeb32a8e1eb 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -291,6 +291,8 @@ static int tracepoint_add_func(struct tracepoint *tp, lockdep_is_held(&tracepoints_mutex)); old = func_add(&tp_funcs, func, prio); if (IS_ERR(old)) { + if (tp->ext && tp->ext->unregfunc && !static_key_enabled(&tp->key)) + tp->ext->unregfunc(); WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); return PTR_ERR(old); } -- cgit 1.3-korg
342829e042actracepoint: balance regfunc() on func_add() failure in tracepoint_add_func()
1 file changed · +2 −1
kernel/tracepoint.c+2 −1 modifieddiff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 91905aa19294d2..dffef52a807bc8 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -300,6 +300,8 @@ static int tracepoint_add_func(struct tracepoint *tp, lockdep_is_held(&tracepoints_mutex)); old = func_add(&tp_funcs, func, prio); if (IS_ERR(old)) { + if (tp->ext && tp->ext->unregfunc && !static_key_enabled(&tp->key)) + tp->ext->unregfunc(); WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); return PTR_ERR(old); } -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing error-path cleanup: when func_add() fails after regfunc() was called during a 0→1 tracepoint transition, the matching unregfunc() is not invoked, leaving subsystem state permanently leaked."
Attack vector
An attacker who can trigger tracepoint registration (e.g., by attaching a BPF program or enabling a tracing event) under memory pressure can cause allocate_probes() inside func_add() to return -ENOMEM. When this happens during a 0→1 transition, tracepoint_add_func() returns the error without calling the subsystem's unregfunc(), leaving regfunc() side effects permanently in place [patch_id=2897855]. For syscall tracepoints, syscall_regfunc() bumps sys_tracepoint_refcount and sets SYSCALL_TRACEPOINT on every task; after a leaked failure the refcount never drops and every task pays syscall entry/exit overhead until reboot [patch_id=2897855]. No authentication is required beyond the ability to register a tracepoint probe.
Affected code
The vulnerability is in the tracepoint_add_func() function in kernel/tracepoint.c [patch_id=2897855]. The error path after a failed func_add() call (line ~300 in the patched version) was missing the corresponding unregfunc() call.
What the fix does
The patch adds a call to tp->ext->unregfunc() (or tp->unregfunc() in backport variants) in the func_add() error path of tracepoint_add_func(), gated on the same condition used during the 1→0 cleanup: the unregfunc pointer must exist and the static key must not already be enabled [patch_id=2897855]. This ensures that when func_add() fails after regfunc() was called, the subsystem's registration side effects are properly undone, making the error path symmetric with the registration path. The fix mirrors the existing 1→0 cleanup logic already present in the function.
Preconditions
- authAbility to register a tracepoint probe (e.g., via BPF or ftrace events)
- inputMemory pressure sufficient to cause allocate_probes() to fail with -ENOMEM
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/247ed8a969f981bfba3112fd4bb441eaa6cef59cnvd
- git.kernel.org/stable/c/2c5b8eeea006eb694c81631cd5713d494b80be90nvd
- git.kernel.org/stable/c/342829e042ac00f3d68d442ea92873fb6683f494nvd
- git.kernel.org/stable/c/7bcadb3c2bc1cf60690e931aadd35fb7bd646a49nvd
- git.kernel.org/stable/c/fad217e16fded7f3c09f8637b0f6a224d58b5f2envd
News mentions
0No linked articles in our index yet.