summaryrefslogtreecommitdiff
path: root/net/ipv4/igmp.c
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2026-07-08 14:41:04 +0200
committerPaolo Abeni <pabeni@redhat.com>2026-07-08 14:41:04 +0200
commit6d27e29a90bc6a717b97c6ddcd866db7bd8e4adc (patch)
tree7a4c1b196df327b7a7320515fb7e6afa3f8aeb1d /net/ipv4/igmp.c
parent235acadd310533ba386ae61ad155b72bee381559 (diff)
parent3546deaa0c30a14c7cdb5dc8f2432cb428f0cd36 (diff)
downloadlinux-next-6d27e29a90bc6a717b97c6ddcd866db7bd8e4adc.tar.gz
linux-next-6d27e29a90bc6a717b97c6ddcd866db7bd8e4adc.zip
Merge branch 'ipv4-ipv6-fix-uaf-and-memory-leak-in-igmp-mld'
Eric Dumazet says: ==================== ipv4/ipv6: Fix UAF and memory leak in IGMP/MLD This series addresses two potential UAF vulnerabilities and memory leaks in the IPv4 IGMP and IPv6 MLD subsystems. The first two patches fix a UAF where the packet receive path races with device teardown. If the device refcount has already hit 0 (but the memory is still held by RCU), incoming IGMP/MLD packets trying to schedule delayed work or timers would call refcount_inc() on the 0 refcount, triggering a warning and eventually leading to a UAF when the work runs after the device has been freed. This is fixed by introducing safe hold helpers using refcount_inc_not_zero(). In MLD, we also ensure we only enqueue the skb if we successfully acquired the device reference, to avoid leaking skbs when the device is being destroyed. The third patch fixes memory leaks in IPv4 IGMP when timers are deleted or stopped. When a timer is deleted (in igmp_mod_timer) or stopped (in igmp_stop_timer) and not re-armed, the code dropped the group refcount using refcount_dec(). However, if the group was concurrently removed from the list, this decrement could drop the refcount to 0 without triggering the cleanup/free path, leaking the group structure. This is fixed by using ip_ma_put() instead, and deferring the put until after the lock is released. ==================== Link: https://patch.msgid.link/20260705181756.963063-1-edumazet@google.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Diffstat (limited to 'net/ipv4/igmp.c')
-rw-r--r--net/ipv4/igmp.c28
1 files changed, 21 insertions, 7 deletions
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index d520ea4f6d14..bb2d4441a492 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -217,13 +217,18 @@ static void ip_sf_list_clear_all(struct ip_sf_list *psf)
static void igmp_stop_timer(struct ip_mc_list *im)
{
+ bool put = false;
+
spin_lock_bh(&im->lock);
if (timer_delete(&im->timer))
- refcount_dec(&im->refcnt);
+ put = true;
WRITE_ONCE(im->tm_running, 0);
WRITE_ONCE(im->reporter, 0);
im->unsolicit_count = 0;
spin_unlock_bh(&im->lock);
+
+ if (put)
+ ip_ma_put(im);
}
/* It must be called with locked im->lock */
@@ -248,20 +253,26 @@ 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)
{
+ bool put = false;
+
spin_lock_bh(&im->lock);
im->unsolicit_count = 0;
if (timer_delete(&im->timer)) {
@@ -271,10 +282,13 @@ static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
spin_unlock_bh(&im->lock);
return;
}
- refcount_dec(&im->refcnt);
+ put = true;
}
igmp_start_timer(im, max_delay);
spin_unlock_bh(&im->lock);
+
+ if (put)
+ ip_ma_put(im);
}