]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 5.10
authorSasha Levin <sashal@kernel.org>
Sat, 28 Dec 2024 23:39:31 +0000 (18:39 -0500)
committerSasha Levin <sashal@kernel.org>
Sat, 28 Dec 2024 23:39:31 +0000 (18:39 -0500)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-5.10/bpf-check-validity-of-link-type-in-bpf_link_show_fdi.patch [new file with mode: 0644]
queue-5.10/ipv6-fix-possible-uaf-in-ip6_finish_output2.patch [new file with mode: 0644]
queue-5.10/ipv6-use-skb_expand_head-in-ip6_finish_output2.patch [new file with mode: 0644]
queue-5.10/ipv6-use-skb_expand_head-in-ip6_xmit.patch [new file with mode: 0644]
queue-5.10/mips-probe-toolchain-support-of-msym32.patch [new file with mode: 0644]
queue-5.10/series
queue-5.10/skbuff-introduce-skb_expand_head.patch [new file with mode: 0644]

diff --git a/queue-5.10/bpf-check-validity-of-link-type-in-bpf_link_show_fdi.patch b/queue-5.10/bpf-check-validity-of-link-type-in-bpf_link_show_fdi.patch
new file mode 100644 (file)
index 0000000..de6fc7c
--- /dev/null
@@ -0,0 +1,60 @@
+From 2450c201d8434f95f15516984c449ffd70aca2bc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 27 Dec 2024 14:04:35 +0800
+Subject: bpf: Check validity of link->type in bpf_link_show_fdinfo()
+
+From: Hou Tao <houtao1@huawei.com>
+
+commit 8421d4c8762bd022cb491f2f0f7019ef51b4f0a7 upstream.
+
+If a newly-added link type doesn't invoke BPF_LINK_TYPE(), accessing
+bpf_link_type_strs[link->type] may result in an out-of-bounds access.
+
+To spot such missed invocations early in the future, checking the
+validity of link->type in bpf_link_show_fdinfo() and emitting a warning
+when such invocations are missed.
+
+Signed-off-by: Hou Tao <houtao1@huawei.com>
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/bpf/20241024013558.1135167-3-houtao@huaweicloud.com
+[ shung-hsi.yu: break up existing seq_printf() call since commit 68b04864ca42
+  ("bpf: Create links for BPF struct_ops maps.") is not present ]
+Signed-off-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/bpf/syscall.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
+index fbe7f8e2b022..b5d9bba73834 100644
+--- a/kernel/bpf/syscall.c
++++ b/kernel/bpf/syscall.c
+@@ -2427,16 +2427,21 @@ static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
+ {
+       const struct bpf_link *link = filp->private_data;
+       const struct bpf_prog *prog = link->prog;
++      enum bpf_link_type type = link->type;
+       char prog_tag[sizeof(prog->tag) * 2 + 1] = { };
++      if (type < ARRAY_SIZE(bpf_link_type_strs) && bpf_link_type_strs[type]) {
++              seq_printf(m, "link_type:\t%s\n", bpf_link_type_strs[type]);
++      } else {
++              WARN_ONCE(1, "missing BPF_LINK_TYPE(...) for link type %u\n", type);
++              seq_printf(m, "link_type:\t<%u>\n", type);
++      }
++      seq_printf(m, "link_id:\t%u\n", link->id);
++
+       bin2hex(prog_tag, prog->tag, sizeof(prog->tag));
+       seq_printf(m,
+-                 "link_type:\t%s\n"
+-                 "link_id:\t%u\n"
+                  "prog_tag:\t%s\n"
+                  "prog_id:\t%u\n",
+-                 bpf_link_type_strs[link->type],
+-                 link->id,
+                  prog_tag,
+                  prog->aux->id);
+       if (link->ops->show_fdinfo)
+-- 
+2.39.5
+
diff --git a/queue-5.10/ipv6-fix-possible-uaf-in-ip6_finish_output2.patch b/queue-5.10/ipv6-fix-possible-uaf-in-ip6_finish_output2.patch
new file mode 100644 (file)
index 0000000..9f4cc40
--- /dev/null
@@ -0,0 +1,52 @@
+From c4385716fd13bf7c5fa96dab92028f4298c2d47f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Dec 2024 21:16:24 -0800
+Subject: ipv6: fix possible UAF in ip6_finish_output2()
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ Upstream commit e891b36de161fcd96f12ff83667473e5067b9037 ]
+
+If skb_expand_head() returns NULL, skb has been freed
+and associated dst/idev could also have been freed.
+
+We need to hold rcu_read_lock() to make sure the dst and
+associated idev are alive.
+
+Fixes: 5796015fa968 ("ipv6: allocate enough headroom in ip6_finish_output2()")
+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-3-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+(cherry picked from commit e891b36de161fcd96f12ff83667473e5067b9037)
+Signed-off-by: Harshvardhan Jha <harshvardhan.j.jha@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/ip6_output.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index a8475848d038..48f926157ef8 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -69,11 +69,15 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
+       /* Be paranoid, rather than too clever. */
+       if (unlikely(hh_len > skb_headroom(skb)) && dev->header_ops) {
++              /* Make sure idev stays alive */
++              rcu_read_lock();
+               skb = skb_expand_head(skb, hh_len);
+               if (!skb) {
+                       IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
++                      rcu_read_unlock();
+                       return -ENOMEM;
+               }
++              rcu_read_unlock();
+       }
+       hdr = ipv6_hdr(skb);
+-- 
+2.39.5
+
diff --git a/queue-5.10/ipv6-use-skb_expand_head-in-ip6_finish_output2.patch b/queue-5.10/ipv6-use-skb_expand_head-in-ip6_finish_output2.patch
new file mode 100644 (file)
index 0000000..1037dab
--- /dev/null
@@ -0,0 +1,130 @@
+From 1a90731ec228f67086bdf801481bbc36936e0b4a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Dec 2024 21:16:22 -0800
+Subject: ipv6: use skb_expand_head in ip6_finish_output2
+
+From: Vasily Averin <vvs@virtuozzo.com>
+
+[ Upstream commit e415ed3a4b8b246ee5e9d109ff5153efcf96b9f2 ]
+
+Unlike skb_realloc_headroom, new helper skb_expand_head does not allocate
+a new skb if possible.
+
+Additionally this patch replaces commonly used dereferencing with variables.
+
+Signed-off-by: Vasily Averin <vvs@virtuozzo.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit e415ed3a4b8b246ee5e9d109ff5153efcf96b9f2)
+Signed-off-by: Harshvardhan Jha <harshvardhan.j.jha@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/ip6_output.c | 51 ++++++++++++++-----------------------------
+ 1 file changed, 16 insertions(+), 35 deletions(-)
+
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index 26d8105981e9..7806963b4539 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -60,46 +60,29 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
+ {
+       struct dst_entry *dst = skb_dst(skb);
+       struct net_device *dev = dst->dev;
++      struct inet6_dev *idev = ip6_dst_idev(dst);
+       unsigned int hh_len = LL_RESERVED_SPACE(dev);
+-      int delta = hh_len - skb_headroom(skb);
+-      const struct in6_addr *nexthop;
++      const struct in6_addr *daddr, *nexthop;
++      struct ipv6hdr *hdr;
+       struct neighbour *neigh;
+       int ret;
+       /* Be paranoid, rather than too clever. */
+-      if (unlikely(delta > 0) && dev->header_ops) {
+-              /* pskb_expand_head() might crash, if skb is shared */
+-              if (skb_shared(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);
+-                      }
+-                      skb = nskb;
+-              }
+-              if (skb &&
+-                  pskb_expand_head(skb, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC)) {
+-                      kfree_skb(skb);
+-                      skb = NULL;
+-              }
++      if (unlikely(hh_len > skb_headroom(skb)) && dev->header_ops) {
++              skb = skb_expand_head(skb, hh_len);
+               if (!skb) {
+-                      IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTDISCARDS);
++                      IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
+                       return -ENOMEM;
+               }
+       }
+-      if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) {
+-              struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
+-
++      hdr = ipv6_hdr(skb);
++      daddr = &hdr->daddr;
++      if (ipv6_addr_is_multicast(daddr)) {
+               if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) &&
+                   ((mroute6_is_socket(net, skb) &&
+                    !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||
+-                   ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr,
+-                                       &ipv6_hdr(skb)->saddr))) {
++                   ipv6_chk_mcast_addr(dev, daddr, &hdr->saddr))) {
+                       struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+                       /* Do not check for IFF_ALLMULTI; multicast routing
+@@ -110,7 +93,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
+                                       net, sk, newskb, NULL, newskb->dev,
+                                       dev_loopback_xmit);
+-                      if (ipv6_hdr(skb)->hop_limit == 0) {
++                      if (hdr->hop_limit == 0) {
+                               IP6_INC_STATS(net, idev,
+                                             IPSTATS_MIB_OUTDISCARDS);
+                               kfree_skb(skb);
+@@ -119,9 +102,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
+               }
+               IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, skb->len);
+-
+-              if (IPV6_ADDR_MC_SCOPE(&ipv6_hdr(skb)->daddr) <=
+-                  IPV6_ADDR_SCOPE_NODELOCAL &&
++              if (IPV6_ADDR_MC_SCOPE(daddr) <= IPV6_ADDR_SCOPE_NODELOCAL &&
+                   !(dev->flags & IFF_LOOPBACK)) {
+                       kfree_skb(skb);
+                       return 0;
+@@ -136,10 +117,10 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
+       }
+       rcu_read_lock_bh();
+-      nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr);
+-      neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
++      nexthop = rt6_nexthop((struct rt6_info *)dst, daddr);
++      neigh = __ipv6_neigh_lookup_noref(dev, nexthop);
+       if (unlikely(!neigh))
+-              neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false);
++              neigh = __neigh_create(&nd_tbl, nexthop, dev, false);
+       if (!IS_ERR(neigh)) {
+               sock_confirm_neigh(skb, neigh);
+               ret = neigh_output(neigh, skb, false);
+@@ -148,7 +129,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
+       }
+       rcu_read_unlock_bh();
+-      IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
++      IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTNOROUTES);
+       kfree_skb(skb);
+       return -EINVAL;
+ }
+-- 
+2.39.5
+
diff --git a/queue-5.10/ipv6-use-skb_expand_head-in-ip6_xmit.patch b/queue-5.10/ipv6-use-skb_expand_head-in-ip6_xmit.patch
new file mode 100644 (file)
index 0000000..5d7de0a
--- /dev/null
@@ -0,0 +1,98 @@
+From f9042eb743dcd072a62a22fcf4cd71304b3176f0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Dec 2024 21:16:23 -0800
+Subject: ipv6: use skb_expand_head in ip6_xmit
+
+From: Vasily Averin <vvs@virtuozzo.com>
+
+[ Upstream commit 0c9f227bee11910a49e1d159abe102d06e3745d5 ]
+
+Unlike skb_realloc_headroom, new helper skb_expand_head
+does not allocate a new skb if possible.
+
+Additionally this patch replaces commonly used dereferencing with variables.
+
+Signed-off-by: Vasily Averin <vvs@virtuozzo.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit 0c9f227bee11910a49e1d159abe102d06e3745d5)
+Signed-off-by: Harshvardhan Jha <harshvardhan.j.jha@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/ipv6/ip6_output.c | 27 +++++++++++----------------
+ 1 file changed, 11 insertions(+), 16 deletions(-)
+
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index 7806963b4539..a8475848d038 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -254,6 +254,8 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
+       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);
+       unsigned int head_room;
+       struct ipv6hdr *hdr;
+       u8  proto = fl6->flowi6_proto;
+@@ -261,22 +263,16 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
+       int hlimit = -1;
+       u32 mtu;
+-      head_room = sizeof(struct ipv6hdr) + LL_RESERVED_SPACE(dst->dev);
++      head_room = sizeof(struct ipv6hdr) + LL_RESERVED_SPACE(dev);
+       if (opt)
+               head_room += opt->opt_nflen + opt->opt_flen;
+-      if (unlikely(skb_headroom(skb) < head_room)) {
+-              struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
+-              if (!skb2) {
+-                      IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
+-                                    IPSTATS_MIB_OUTDISCARDS);
+-                      kfree_skb(skb);
++      if (unlikely(head_room > skb_headroom(skb))) {
++              skb = skb_expand_head(skb, head_room);
++              if (!skb) {
++                      IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
+                       return -ENOBUFS;
+               }
+-              if (skb->sk)
+-                      skb_set_owner_w(skb2, skb->sk);
+-              consume_skb(skb);
+-              skb = skb2;
+       }
+       if (opt) {
+@@ -318,8 +314,7 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
+       mtu = dst_mtu(dst);
+       if ((skb->len <= mtu) || skb->ignore_df || skb_is_gso(skb)) {
+-              IP6_UPD_PO_STATS(net, ip6_dst_idev(skb_dst(skb)),
+-                            IPSTATS_MIB_OUT, skb->len);
++              IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
+               /* if egress device is enslaved to an L3 master device pass the
+                * skb to its handler for processing
+@@ -332,17 +327,17 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
+                * we promote our socket to non const
+                */
+               return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
+-                             net, (struct sock *)sk, skb, NULL, dst->dev,
++                             net, (struct sock *)sk, skb, NULL, dev,
+                              dst_output);
+       }
+-      skb->dev = dst->dev;
++      skb->dev = dev;
+       /* ipv6_local_error() does not require socket lock,
+        * we promote our socket to non const
+        */
+       ipv6_local_error((struct sock *)sk, EMSGSIZE, fl6, mtu);
+-      IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_FRAGFAILS);
++      IP6_INC_STATS(net, idev, IPSTATS_MIB_FRAGFAILS);
+       kfree_skb(skb);
+       return -EMSGSIZE;
+ }
+-- 
+2.39.5
+
diff --git a/queue-5.10/mips-probe-toolchain-support-of-msym32.patch b/queue-5.10/mips-probe-toolchain-support-of-msym32.patch
new file mode 100644 (file)
index 0000000..393d3f0
--- /dev/null
@@ -0,0 +1,38 @@
+From f98f5c3de0d2365a4e0063275abe8ea0e8c4abeb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Dec 2024 14:09:18 +0800
+Subject: MIPS: Probe toolchain support of -msym32
+
+From: Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+[ Upstream commit 18ca63a2e23c5e170d2d7552b64b1f5ad019cd9b ]
+
+msym32 is not supported by LLVM toolchain.
+Workaround by probe toolchain support of msym32 for KBUILD_SYM32
+feature.
+
+Link: https://github.com/ClangBuiltLinux/linux/issues/1544
+Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+Signed-off-by: WangYuli <wangyuli@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/mips/Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/mips/Makefile b/arch/mips/Makefile
+index acab8018ab44..289fb4b88d0e 100644
+--- a/arch/mips/Makefile
++++ b/arch/mips/Makefile
+@@ -272,7 +272,7 @@ drivers-$(CONFIG_PCI)              += arch/mips/pci/
+ ifdef CONFIG_64BIT
+   ifndef KBUILD_SYM32
+     ifeq ($(shell expr $(load-y) \< 0xffffffff80000000), 0)
+-      KBUILD_SYM32 = y
++      KBUILD_SYM32 = $(call cc-option-yn, -msym32)
+     endif
+   endif
+-- 
+2.39.5
+
index f43659329d9d6f71cd9e4f019c387d5f6044e674..8373322e439e688219e6761fde7df279919d2c07 100644 (file)
@@ -65,3 +65,9 @@ platform-x86-asus-nb-wmi-ignore-unknown-event-0xcf.patch
 scsi-mpt3sas-diag-reset-when-doorbell-in-use-bit-is-.patch
 scsi-storvsc-do-not-flag-maintenance_in-return-of-sr.patch
 virtio-blk-don-t-keep-queue-frozen-during-system-sus.patch
+mips-probe-toolchain-support-of-msym32.patch
+skbuff-introduce-skb_expand_head.patch
+ipv6-use-skb_expand_head-in-ip6_finish_output2.patch
+ipv6-use-skb_expand_head-in-ip6_xmit.patch
+ipv6-fix-possible-uaf-in-ip6_finish_output2.patch
+bpf-check-validity-of-link-type-in-bpf_link_show_fdi.patch
diff --git a/queue-5.10/skbuff-introduce-skb_expand_head.patch b/queue-5.10/skbuff-introduce-skb_expand_head.patch
new file mode 100644 (file)
index 0000000..f2cba7b
--- /dev/null
@@ -0,0 +1,94 @@
+From 804c67eb0b72c6906181767deaa9fc0963a1ae5b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Dec 2024 21:16:21 -0800
+Subject: skbuff: introduce skb_expand_head()
+
+From: Vasily Averin <vvs@virtuozzo.com>
+
+[ Upstream commit f1260ff15a71b8fc122b2c9abd8a7abffb6e0168 ]
+
+Like skb_realloc_headroom(), new helper increases headroom of specified skb.
+Unlike skb_realloc_headroom(), it does not allocate a new skb if possible;
+copies skb->sk on new skb when as needed and frees original skb in case
+of failures.
+
+This helps to simplify ip[6]_finish_output2() and a few other similar cases.
+
+Signed-off-by: Vasily Averin <vvs@virtuozzo.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit f1260ff15a71b8fc122b2c9abd8a7abffb6e0168)
+Signed-off-by: Harshvardhan Jha <harshvardhan.j.jha@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/skbuff.h |  1 +
+ net/core/skbuff.c      | 42 ++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 43 insertions(+)
+
+diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
+index 31ae4b74d435..3248e4aeec03 100644
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -1166,6 +1166,7 @@ static inline struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom,
+ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, gfp_t gfp_mask);
+ struct sk_buff *skb_realloc_headroom(struct sk_buff *skb,
+                                    unsigned int headroom);
++struct sk_buff *skb_expand_head(struct sk_buff *skb, unsigned int headroom);
+ struct sk_buff *skb_copy_expand(const struct sk_buff *skb, int newheadroom,
+                               int newtailroom, gfp_t priority);
+ int __must_check skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg,
+diff --git a/net/core/skbuff.c b/net/core/skbuff.c
+index b0c2d6f01800..fa3ea287d6ec 100644
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -1732,6 +1732,48 @@ struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom)
+ }
+ EXPORT_SYMBOL(skb_realloc_headroom);
++/**
++ *    skb_expand_head - reallocate header of &sk_buff
++ *    @skb: buffer to reallocate
++ *    @headroom: needed headroom
++ *
++ *    Unlike skb_realloc_headroom, this one does not allocate a new skb
++ *    if possible; copies skb->sk to new skb as needed
++ *    and frees original skb in case of failures.
++ *
++ *    It expect increased headroom and generates warning otherwise.
++ */
++
++struct sk_buff *skb_expand_head(struct sk_buff *skb, unsigned int headroom)
++{
++      int delta = headroom - skb_headroom(skb);
++
++      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)) {
++              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);
++              }
++              skb = nskb;
++      }
++      if (skb &&
++          pskb_expand_head(skb, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC)) {
++              kfree_skb(skb);
++              skb = NULL;
++      }
++      return skb;
++}
++EXPORT_SYMBOL(skb_expand_head);
++
+ /**
+  *    skb_copy_expand -       copy and expand sk_buff
+  *    @skb: buffer to copy
+-- 
+2.39.5
+