--- /dev/null
+From 2d5ff7e339d04622d8282661df36151906d0e1c7 Mon Sep 17 00:00:00 2001
+From: Eric Dumazet <edumazet@google.com>
+Date: Tue, 20 Aug 2024 16:08:59 +0000
+Subject: ipv6: prevent possible UAF in ip6_xmit()
+
+From: Eric Dumazet <edumazet@google.com>
+
+commit 2d5ff7e339d04622d8282661df36151906d0e1c7 upstream.
+
+If skb_expand_head() returns NULL, skb has been freed
+and the associated dst/idev could also have been freed.
+
+We must use rcu_read_lock() to prevent a possible UAF.
+
+Fixes: 0c9f227bee11 ("ipv6: use skb_expand_head in ip6_xmit")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Cc: Vasily Averin <vasily.averin@linux.dev>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Link: https://patch.msgid.link/20240820160859.3786976-4-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/ipv6/ip6_output.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -271,11 +271,15 @@ int ip6_xmit(const struct sock *sk, stru
+ head_room += opt->opt_nflen + opt->opt_flen;
+
+ if (unlikely(head_room > skb_headroom(skb))) {
++ /* Make sure idev stays alive */
++ 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;
+ }
++ rcu_read_unlock();
+ }
+
+ if (opt) {
--- /dev/null
+From 7f678def99d29c520418607509bb19c7fc96a6db Mon Sep 17 00:00:00 2001
+From: Vasily Averin <vvs@virtuozzo.com>
+Date: Fri, 22 Oct 2021 13:28:37 +0300
+Subject: skb_expand_head() adjust skb->truesize incorrectly
+
+From: Vasily Averin <vvs@virtuozzo.com>
+
+commit 7f678def99d29c520418607509bb19c7fc96a6db upstream.
+
+Christoph Paasch reports [1] about incorrect skb->truesize
+after skb_expand_head() call in ip6_xmit.
+This may happen because of two reasons:
+- skb_set_owner_w() for newly cloned skb is called too early,
+before pskb_expand_head() where truesize is adjusted for (!skb-sk) case.
+- pskb_expand_head() does not adjust truesize in (skb->sk) case.
+In this case sk->sk_wmem_alloc should be adjusted too.
+
+[1] https://lkml.org/lkml/2021/8/20/1082
+
+Fixes: f1260ff15a71 ("skbuff: introduce skb_expand_head()")
+Fixes: 2d85a1b31dde ("ipv6: ip6_finish_output2: set sk into newly allocated nskb")
+Reported-by: Christoph Paasch <christoph.paasch@gmail.com>
+Signed-off-by: Vasily Averin <vvs@virtuozzo.com>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Link: https://lore.kernel.org/r/644330dd-477e-0462-83bf-9f514c41edd1@virtuozzo.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/core/skbuff.c | 36 +++++++++++++++++++++++-------------
+ 1 file changed, 23 insertions(+), 13 deletions(-)
+ create mode 100644 net/core/sock_destructor.h
+
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -77,6 +77,7 @@
+ #include <linux/indirect_call_wrapper.h>
+
+ #include "datagram.h"
++#include "sock_destructor.h"
+
+ struct kmem_cache *skbuff_head_cache __ro_after_init;
+ static struct kmem_cache *skbuff_fclone_cache __ro_after_init;
+@@ -1741,30 +1742,39 @@ EXPORT_SYMBOL(skb_realloc_headroom);
+ struct sk_buff *skb_expand_head(struct sk_buff *skb, unsigned int headroom)
+ {
+ int delta = headroom - skb_headroom(skb);
++ int osize = skb_end_offset(skb);
++ struct sock *sk = skb->sk;
+
+ if (WARN_ONCE(delta <= 0,
+ "%s is expecting an increase in the headroom", __func__))
+ return skb;
+
+- /* pskb_expand_head() might crash, if skb is shared */
+- if (skb_shared(skb)) {
++ delta = SKB_DATA_ALIGN(delta);
++ /* pskb_expand_head() might crash, if skb is shared. */
++ if (skb_shared(skb) || !is_skb_wmem(skb)) {
+ struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
+
+- if (likely(nskb)) {
+- if (skb->sk)
+- skb_set_owner_w(nskb, skb->sk);
+- consume_skb(skb);
+- } else {
+- kfree_skb(skb);
+- }
++ if (unlikely(!nskb))
++ goto fail;
++
++ if (sk)
++ skb_set_owner_w(nskb, sk);
++ consume_skb(skb);
+ skb = nskb;
+ }
+- if (skb &&
+- pskb_expand_head(skb, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC)) {
+- kfree_skb(skb);
+- skb = NULL;
++ if (pskb_expand_head(skb, delta, 0, GFP_ATOMIC))
++ goto fail;
++
++ if (sk && is_skb_wmem(skb)) {
++ delta = skb_end_offset(skb) - osize;
++ refcount_add(delta, &sk->sk_wmem_alloc);
++ skb->truesize += delta;
+ }
+ return skb;
++
++fail:
++ kfree_skb(skb);
++ return NULL;
+ }
+ EXPORT_SYMBOL(skb_expand_head);
+