Use-After-Free (UAF) in IGMP timer handling around in_device during concurrent teardown

HIGH
torvalds/linux
Commit: 7b19c0f81ed1
Affected: v7.0-rc6 and earlier (mainline before this commit)
2026-07-17 16:20 UTC

Description

The commit fixes a race between device teardown (inetdev_destroy) and IGMP processing that could cause a use-after-free of struct in_device in the IGMP timer path. Specifically, igmp_gq_start_timer() and related timer arming paths could re-arm timers while the underlying in_device is being freed under RCU grace, leading to a dereference of freed memory when the timer fires. The fix adds in_dev_hold_safe() (wrapping refcount_inc_not_zero) and only arms the timer if a safe non-zero refcount increment succeeds; otherwise the timer is not armed. This prevents acquiring a reference to an in_device that is already being destroyed, eliminating the potential UAF. A similar issue was fixed for IPv6 MLD in a separate patch. Impact: Use-After-Free (UAF) in the IGMP timer handling code due to a race with inetdev_destroy. The vulnerability could cause kernel panics (DoS) and, in theory, memory safety issues if exploited in certain conditions. The commit is a genuine fix for this issue, not just a dependency or formatting change.

Proof of Concept

High-level PoC (non-executable): In a running system, arrange a race where an interface/device is being torn down (inetdev_destroy) while an IGMP query processing path is active and IGMP timers are being re-armed. Before the fix, igmp_gq_start_timer() could call in_dev_hold() and increment the refcount from 0 to 1 even as the in_device is scheduled for destruction, causing a use-after-free when the timer later fires and dereferences in_device. With the fix, in_dev_hold_safe() uses refcount_inc_not_zero, so if the refcount already hit 0, the function fails and the timer is not armed. The net effect is that the attacker cannot safely trigger a timer re-arm on a dying in_device, preventing the UAF. This description omits exploit code and concrete steps to avoid enabling misuse; it provides the conceptual attack surface and mitigation.

Commit Details

Author: Eric Dumazet

Date: 2026-07-05 18:17 UTC

Message:

ipv4: igmp: Fix potential UAF in igmp_gq_start_timer() A race condition exists between device teardown (inetdev_destroy) and incoming IGMP query processing (igmp_rcv), leading to a Use-After-Free in the IGMP timer callback. During device destruction, inetdev_destroy() drops the primary reference to in_device, which can drop its refcount to 0. The actual freeing of in_device memory is deferred via RCU (using call_rcu()). Concurrently, igmp_rcv() runs under RCU read lock and obtains the in_device pointer. Because the memory is RCU-protected, CPU-0 can safely dereference in_device even if its refcount has hit 0. However, if CPU-0 calls igmp_gq_start_timer() and re-arms the timer, it attempts to acquire a reference using in_dev_hold(). This increments the refcount from 0 to 1, triggering a "refcount_t: addition on 0" warning. Since the in_device memory is still scheduled to be freed after the RCU grace period (as the free callback does not check the refcount again), the device is freed while the timer is still armed. When the timer expires, it accesses the freed memory, causing a kernel panic. Fix this by using refcount_inc_not_zero() (via a new helper in_dev_hold_safe()) to prevent acquiring a reference if the device is already being destroyed. If the refcount is 0, we do not arm the timer. A similar issue in IPv6 MLD is fixed in a subsequent patch. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Zero Day Initiative <zdi-disclosures@trendmicro.com> Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: Ido Schimmel <idosch@nvidia.com> Link: https://patch.msgid.link/20260705181756.963063-2-edumazet@google.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>

Triage Assessment

Vulnerability Type: Use-After-Free

Confidence: HIGH

Reasoning:

The commit addresses a race between device teardown and IGMP timer handling that could lead to a use-after-free when a timer accesses in_device memory after it has been freed. By ensuring timer arming only occurs when the device refcount is non-zero (via in_dev_hold_safe) and preventing new references when the device is being destroyed, it mitigates a memory safety vulnerability (use-after-free) with potential kernel panics or security implications.

Verification Assessment

Vulnerability Type: Use-After-Free (UAF) in IGMP timer handling around in_device during concurrent teardown

Confidence: HIGH

Affected Versions: v7.0-rc6 and earlier (mainline before this commit)

Code Diff

diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index dccbeb25f70141..6032eea2539a60 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -293,6 +293,11 @@ static inline void in_dev_put(struct in_device *idev) #define __in_dev_put(idev) refcount_dec(&(idev)->refcnt) #define in_dev_hold(idev) refcount_inc(&(idev)->refcnt) +static inline bool in_dev_hold_safe(struct in_device *idev) +{ + return refcount_inc_not_zero(&idev->refcnt); +} + #endif /* __KERNEL__ */ static __inline__ __be32 inet_make_mask(int logmask) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index d520ea4f6d142d..3a1cb2a827f396 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -248,16 +248,20 @@ static void igmp_gq_start_timer(struct in_device *in_dev) return; in_dev->mr_gq_running = 1; - if (!mod_timer(&in_dev->mr_gq_timer, exp)) - in_dev_hold(in_dev); + if (in_dev_hold_safe(in_dev)) { + if (mod_timer(&in_dev->mr_gq_timer, exp)) + in_dev_put(in_dev); + } } static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) { - int tv = get_random_u32_below(delay); + if (in_dev_hold_safe(in_dev)) { + int tv = get_random_u32_below(delay); - if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2)) - in_dev_hold(in_dev); + if (mod_timer(&in_dev->mr_ifc_timer, jiffies + tv + 2)) + in_dev_put(in_dev); + } } static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
← Back to Alerts View on GitHub →