From: Ido Schimmel <idosch@nvidia.com>
To: Eric Dumazet <edumazet@google.com>
Cc: "David S . Miller" <davem@davemloft.net>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Simon Horman <horms@kernel.org>,
Kuniyuki Iwashima <kuniyu@google.com>,
David Ahern <dsahern@kernel.org>,
netdev@vger.kernel.org, eric.dumazet@gmail.com
Subject: Re: [PATCH net 2/3] ipv6: mcast: Fix potential UAF in MLD delayed work
Date: Sun, 5 Jul 2026 13:53:49 +0300 [thread overview]
Message-ID: <20260705105349.GA172278@shredder> (raw)
In-Reply-To: <20260704194346.4065071-3-edumazet@google.com>
On Sat, Jul 04, 2026 at 07:43:45PM +0000, Eric Dumazet wrote:
> A race condition exists between device teardown and incoming MLD query
> processing, leading to a Use-After-Free in the MLD delayed work.
>
> During device destruction, the primary reference to inet6_dev is dropped,
> which can drop its refcount to 0. The actual freeing of inet6_dev memory
> is deferred via RCU.
>
> Concurrently, the packet receive path runs under RCU read lock and obtains
> the inet6_dev pointer. Because the memory is RCU-protected, CPU-0 can
> safely dereference inet6_dev even if its refcount has hit 0.
>
> However, if CPU-0 calls igmp6_event_query() and schedules delayed work, it
> attempts to acquire a reference using in6_dev_hold(). This increments the
> refcount from 0 to 1, triggering a "refcount_t: addition on 0" warning.
> Since the inet6_dev memory is still scheduled to be freed after the RCU
> grace period, the device is freed while the work is still scheduled.
> When the work runs, it accesses the freed memory, causing a kernel panic.
>
> Fix this by using refcount_inc_not_zero() (via a new helper
> in6_dev_hold_safe()) to prevent acquiring a reference if the device is
> already being destroyed. If the refcount is 0, we do not schedule the work.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Signed-off-by: Eric Dumazet <edumazet@google.com>
Eric, thanks for taking care of this!
> ---
> include/net/addrconf.h | 5 +++++
> net/ipv6/mcast.c | 38 ++++++++++++++++++++++++++++----------
> 2 files changed, 33 insertions(+), 10 deletions(-)
[...]
> @@ -1395,6 +1401,7 @@ static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld,
> void igmp6_event_query(struct sk_buff *skb)
> {
> struct inet6_dev *idev = __in6_dev_get(skb->dev);
> + bool put = false;
>
> if (!idev || idev->dead)
> goto out;
> @@ -1402,11 +1409,16 @@ void igmp6_event_query(struct sk_buff *skb)
> spin_lock_bh(&idev->mc_query_lock);
> if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS) {
> __skb_queue_tail(&idev->mc_query_queue, skb);
Shouldn't we only enqueue the skb if we managed to take a reference?
Something like [1] (on top of this patch).
If we failed to take a reference, then ipv6_mc_destroy_dev() already ran
and the queue will not be purged, thereby leaking this skb.
> - if (!mod_delayed_work(mld_wq, &idev->mc_query_work, 0))
> - in6_dev_hold(idev);
> + if (in6_dev_hold_safe(idev)) {
> + if (mod_delayed_work(mld_wq, &idev->mc_query_work, 0))
> + put = true;
> + }
> skb = NULL;
> }
> spin_unlock_bh(&idev->mc_query_lock);
> +
> + if (put)
> + in6_dev_put(idev);
> out:
> kfree_skb(skb);
> }
> @@ -1570,6 +1582,7 @@ static void mld_query_work(struct work_struct *work)
> void igmp6_event_report(struct sk_buff *skb)
> {
> struct inet6_dev *idev = __in6_dev_get(skb->dev);
> + bool put = false;
>
> if (!idev || idev->dead)
> goto out;
> @@ -1577,11 +1590,16 @@ void igmp6_event_report(struct sk_buff *skb)
> spin_lock_bh(&idev->mc_report_lock);
> if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS) {
> __skb_queue_tail(&idev->mc_report_queue, skb);
Same here
> - if (!mod_delayed_work(mld_wq, &idev->mc_report_work, 0))
> - in6_dev_hold(idev);
> + if (in6_dev_hold_safe(idev)) {
> + if (mod_delayed_work(mld_wq, &idev->mc_report_work, 0))
> + put = true;
> + }
> skb = NULL;
> }
> spin_unlock_bh(&idev->mc_report_lock);
> +
> + if (put)
> + in6_dev_put(idev);
> out:
> kfree_skb(skb);
> }
[1]
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 12d22de6f496..aaba4c2aae23 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1408,12 +1408,11 @@ void igmp6_event_query(struct sk_buff *skb)
goto out;
spin_lock_bh(&idev->mc_query_lock);
- if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS) {
+ if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS &&
+ in6_dev_hold_safe(idev)) {
__skb_queue_tail(&idev->mc_query_queue, skb);
- if (in6_dev_hold_safe(idev)) {
- if (mod_delayed_work(mld_wq, &idev->mc_query_work, 0))
- put = true;
- }
+ if (mod_delayed_work(mld_wq, &idev->mc_query_work, 0))
+ put = true;
skb = NULL;
}
spin_unlock_bh(&idev->mc_query_lock);
@@ -1589,12 +1588,11 @@ void igmp6_event_report(struct sk_buff *skb)
goto out;
spin_lock_bh(&idev->mc_report_lock);
- if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS) {
+ if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS &&
+ in6_dev_hold_safe(idev)) {
__skb_queue_tail(&idev->mc_report_queue, skb);
- if (in6_dev_hold_safe(idev)) {
- if (mod_delayed_work(mld_wq, &idev->mc_report_work, 0))
- put = true;
- }
+ if (mod_delayed_work(mld_wq, &idev->mc_report_work, 0))
+ put = true;
skb = NULL;
}
spin_unlock_bh(&idev->mc_report_lock);
next prev parent reply other threads:[~2026-07-05 10:54 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-04 19:43 [PATCH net 0/3] ipv4/ipv6: Fix UAF and memory leak in IGMP/MLD Eric Dumazet
2026-07-04 19:43 ` [PATCH net 1/3] ipv4: igmp: Fix potential UAF in igmp_gq_start_timer() Eric Dumazet
2026-07-05 11:33 ` Ido Schimmel
2026-07-04 19:43 ` [PATCH net 2/3] ipv6: mcast: Fix potential UAF in MLD delayed work Eric Dumazet
2026-07-05 10:53 ` Ido Schimmel [this message]
2026-07-05 13:58 ` Eric Dumazet
2026-07-04 19:43 ` [PATCH net 3/3] ipv4: igmp: Fix potential memory leak in igmp_mod_timer() Eric Dumazet
2026-07-05 11:58 ` Ido Schimmel
2026-07-05 14:03 ` Eric Dumazet
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260705105349.GA172278@shredder \
--to=idosch@nvidia.com \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@google.com \
--cc=eric.dumazet@gmail.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.