From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-qv1-f73.google.com (mail-qv1-f73.google.com [209.85.219.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5F71D39EF05 for ; Sun, 5 Jul 2026 18:18:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.73 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783275484; cv=none; b=AMewVEY3bM0GYMZocuJO8olvYuR44RNCUsL/WXAzdfeFZO6cOFg+kCxrfY+/2yu1dWVDR1rcNhOSu6eRbgId1RZph26qRjAUbK+REXfF82HuxLwqdP8t76KrIV7N3gkcuU07dhR3EXDbOsBfLk61O7BnXwPapLp9HOkTiIkkNjE= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783275484; c=relaxed/simple; bh=Ge6sF6o48Cif9E/2QKG/wZLG7r4xEvDoRj3zBoLRqN8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=EZt8/qsTJ6eFihPRWi2CQg8SZ+wL0cFDGv7/y9dPaWQhzJ/Hk4KEhQaODgwpWw+dR6yuT5VZDdqWgyjaCuw8ElbrY/k4BYKIPbDCJtath01D9BWmQf2ys3zhhWVf6ojF32dNjB5GxEW8v3ZKSozXF/o5S3tXGD/VdfwohNP0m9Y= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--edumazet.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=A5gMf+Io; arc=none smtp.client-ip=209.85.219.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--edumazet.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="A5gMf+Io" Received: by mail-qv1-f73.google.com with SMTP id 6a1803df08f44-8ea75996387so49064126d6.1 for ; Sun, 05 Jul 2026 11:18:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1783275482; x=1783880282; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=uV+UJj5MfP2lWoeicP6DWNwnk3TtaXcEuaGgXFYFzpE=; b=A5gMf+IothJLIOkN2gJoAdXVkBe2FVtrebo+aYIWgRSnXAA27phZCyyRS54xGN/7kM wDZIN7XRz9TlgpbCLikrAxd6qLCfoi29cNQoh5MAkEQph8kMu+DWsDeQTryqwrn/kpIC 4uoubM2rjSdsGCDQ/sRQyTsC9WCKmdVKDfUg+UmTj9vuGCirq0zBLfEiA5HY99VVQuTG Mv//0HGcH5AbmFnPv8RNOg4aJuOdAr15CF5LWDX5BCiQwfxsLFGwQz4OGWUL74O/QArP qJjRBA9LBzcAEFTizaVMhX+ZTJJRyxuXAkt8o3ekCMPA17V0O8RFJUCQhiv4EAKOZbrQ MSew== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783275482; x=1783880282; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=uV+UJj5MfP2lWoeicP6DWNwnk3TtaXcEuaGgXFYFzpE=; b=hnW0UhzjWxWnit64csM1aj539iCoYkvX1mlnQ6d0+n9DxpYCIS7rWuZETu/uAvBJ8n bl5e9K19g2M2grK80QqYNTLIIRALpzEITZhnDqpeRtBVNGx625QWpcyKjlRM8dGLyJy0 6tnA7myv+nZ0+HHuOljW14CFVs9YynpSjzsIC74WBhMuGkwXPd/VjvmVVjJ/GQJjRD1F Gsi/tRMSaz23l0m4aAbeuU7Jq0KYWWa7extjE8z9eUfzOrprDshbFZb/Shq5/hKsavbu WeRoHc3BVx5zwAEyfEJhCjnrHjJHh7ZE+domfp7j9wVSQuNmqeF6xg7unBJP4CtzeIUN ak+w== X-Forwarded-Encrypted: i=1; AHgh+RorBw1nxmbdZDpRYzF81UPaQ62oA7TOwccXG7KtmHzfJogK/VJmM39BEh2PvsS8yP/zYweb/Ac=@vger.kernel.org X-Gm-Message-State: AOJu0YwByIAjVmzDejpZeezEXhQixAE1tFkH4TswlaqXY0+YHgBuSTpv YxnM/v+rlMJ6X270FAC9Ej0gStFfgUcHO3BEGKNgBMo0u/GO6vyEd2w3yJsFmQ4ix5wphqzoP9u ZaMOR/ToTHBAVVA== X-Received: from qvob8.prod.google.com ([2002:a0c:e888:0:b0:8f1:50e7:c4a5]) (user=edumazet job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6214:2b85:b0:8f0:efd0:d5cc with SMTP id 6a1803df08f44-8f74bc93834mr100316126d6.33.1783275482061; Sun, 05 Jul 2026 11:18:02 -0700 (PDT) Date: Sun, 5 Jul 2026 18:17:55 +0000 In-Reply-To: <20260705181756.963063-1-edumazet@google.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260705181756.963063-1-edumazet@google.com> X-Mailer: git-send-email 2.55.0.rc0.799.gd6f94ed593-goog Message-ID: <20260705181756.963063-3-edumazet@google.com> Subject: [PATCH v2 net 2/3] ipv6: mcast: Fix potential UAF in MLD delayed work From: Eric Dumazet To: "David S . Miller" , Jakub Kicinski , Paolo Abeni Cc: Simon Horman , Kuniyuki Iwashima , Ido Schimmel , David Ahern , netdev@vger.kernel.org, eric.dumazet@gmail.com, Eric Dumazet Content-Type: text/plain; charset="UTF-8" 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 --- include/net/addrconf.h | 5 +++++ net/ipv6/mcast.c | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 539bbbe54b14e8108ff7304d7a08bc605655cc31..8ced27a8229b6e0580f934be2223676cc123307b 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -446,6 +446,11 @@ static inline void in6_dev_hold(struct inet6_dev *idev) refcount_inc(&idev->refcnt); } +static inline bool in6_dev_hold_safe(struct inet6_dev *idev) +{ + return refcount_inc_not_zero(&idev->refcnt); +} + /* called with rcu_read_lock held */ static inline bool ip6_ignore_linkdown(const struct net_device *dev) { diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 04b811b3be978e36b0fd4ecd8312313d73ed2ed9..4d2b9377ba2de5ad2b6d503bb4b285bfd1307a55 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1083,8 +1083,10 @@ static void mld_gq_start_work(struct inet6_dev *idev) mc_assert_locked(idev); idev->mc_gq_running = 1; - if (!mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_gq_stop_work(struct inet6_dev *idev) @@ -1102,8 +1104,10 @@ static void mld_ifc_start_work(struct inet6_dev *idev, unsigned long delay) mc_assert_locked(idev); - if (!mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_ifc_stop_work(struct inet6_dev *idev) @@ -1121,8 +1125,10 @@ static void mld_dad_start_work(struct inet6_dev *idev, unsigned long delay) mc_assert_locked(idev); - if (!mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_dad_stop_work(struct inet6_dev *idev) @@ -1395,18 +1401,23 @@ 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; 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 (!mod_delayed_work(mld_wq, &idev->mc_query_work, 0)) - in6_dev_hold(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,18 +1581,23 @@ 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; 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 (!mod_delayed_work(mld_wq, &idev->mc_report_work, 0)) - in6_dev_hold(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); } -- 2.55.0.rc0.799.gd6f94ed593-goog