--- /dev/null
+From 9085e56501d93af9f2d7bd16f7fcfacdde47b99c Mon Sep 17 00:00:00 2001
+From: Eric Dumazet <edumazet@google.com>
+Date: Thu, 28 Aug 2025 19:58:18 +0000
+Subject: ipv6: use RCU in ip6_xmit()
+
+From: Eric Dumazet <edumazet@google.com>
+
+commit 9085e56501d93af9f2d7bd16f7fcfacdde47b99c upstream.
+
+Use RCU in ip6_xmit() in order to use dst_dev_rcu() to prevent
+possible UAF.
+
+Fixes: 4a6ce2b6f2ec ("net: introduce a new function dst_dev_put()")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Link: https://patch.msgid.link/20250828195823.3958522-4-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Keerthana K <keerthana.kalyanasundaram@broadcom.com>
+Signed-off-by: Shivani Agarwal <shivani.agarwal@broadcom.com>
+---
+ net/ipv6/ip6_output.c | 35 +++++++++++++++++++++--------------
+ 1 file changed, 21 insertions(+), 14 deletions(-)
+
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -258,35 +258,36 @@ bool ip6_autoflowlabel(struct net *net,
+ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
+ __u32 mark, struct ipv6_txoptions *opt, int tclass, u32 priority)
+ {
+- struct net *net = sock_net(sk);
+ const struct ipv6_pinfo *np = inet6_sk(sk);
+ struct in6_addr *first_hop = &fl6->daddr;
+ struct dst_entry *dst = skb_dst(skb);
+- struct net_device *dev = dst->dev;
+ struct inet6_dev *idev = ip6_dst_idev(dst);
+ struct hop_jumbo_hdr *hop_jumbo;
+ int hoplen = sizeof(*hop_jumbo);
++ struct net *net = sock_net(sk);
+ unsigned int head_room;
++ struct net_device *dev;
+ struct ipv6hdr *hdr;
+ u8 proto = fl6->flowi6_proto;
+ int seg_len = skb->len;
+- int hlimit = -1;
++ int ret, hlimit = -1;
+ u32 mtu;
+
++ rcu_read_lock();
++
++ dev = dst_dev_rcu(dst);
+ head_room = sizeof(struct ipv6hdr) + hoplen + LL_RESERVED_SPACE(dev);
+ if (opt)
+ head_room += opt->opt_nflen + opt->opt_flen;
+
+ if (unlikely(head_room > skb_headroom(skb))) {
+- /* Make sure idev stays alive */
+- rcu_read_lock();
++ /* idev stays alive while we hold rcu_read_lock(). */
+ skb = skb_expand_head(skb, head_room);
+ if (!skb) {
+ IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
+- rcu_read_unlock();
+- return -ENOBUFS;
++ ret = -ENOBUFS;
++ goto unlock;
+ }
+- rcu_read_unlock();
+ }
+
+ if (opt) {
+@@ -348,17 +349,21 @@ int ip6_xmit(const struct sock *sk, stru
+ * skb to its handler for processing
+ */
+ skb = l3mdev_ip6_out((struct sock *)sk, skb);
+- if (unlikely(!skb))
+- return 0;
++ if (unlikely(!skb)) {
++ ret = 0;
++ goto unlock;
++ }
+
+ /* hooks should never assume socket lock is held.
+ * we promote our socket to non const
+ */
+- return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
+- net, (struct sock *)sk, skb, NULL, dev,
+- dst_output);
++ ret = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
++ net, (struct sock *)sk, skb, NULL, dev,
++ dst_output);
++ goto unlock;
+ }
+
++ ret = -EMSGSIZE;
+ skb->dev = dev;
+ /* ipv6_local_error() does not require socket lock,
+ * we promote our socket to non const
+@@ -367,7 +372,9 @@ int ip6_xmit(const struct sock *sk, stru
+
+ IP6_INC_STATS(net, idev, IPSTATS_MIB_FRAGFAILS);
+ kfree_skb(skb);
+- return -EMSGSIZE;
++unlock:
++ rcu_read_unlock();
++ return ret;
+ }
+ EXPORT_SYMBOL(ip6_xmit);
+