]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 5.4
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.4/epoll-add-synchronous-wakeup-support-for-ep_poll_cal.patch [new file with mode: 0644]
queue-5.4/ipv6-fix-possible-uaf-in-ip6_finish_output2.patch [new file with mode: 0644]
queue-5.4/ipv6-use-skb_expand_head-in-ip6_finish_output2.patch [new file with mode: 0644]
queue-5.4/ipv6-use-skb_expand_head-in-ip6_xmit.patch [new file with mode: 0644]
queue-5.4/mips-probe-toolchain-support-of-msym32.patch [new file with mode: 0644]
queue-5.4/series
queue-5.4/skbuff-introduce-skb_expand_head.patch [new file with mode: 0644]

diff --git a/queue-5.4/epoll-add-synchronous-wakeup-support-for-ep_poll_cal.patch b/queue-5.4/epoll-add-synchronous-wakeup-support-for-ep_poll_cal.patch
new file mode 100644 (file)
index 0000000..d403dc8
--- /dev/null
@@ -0,0 +1,64 @@
+From f8a05f73700a36801f76c3deaede7dcf06f537c5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 26 Dec 2024 19:14:29 +0800
+Subject: epoll: Add synchronous wakeup support for ep_poll_callback
+
+From: Xuewen Yan <xuewen.yan@unisoc.com>
+
+[ Upstream commit 900bbaae67e980945dec74d36f8afe0de7556d5a ]
+
+Now, the epoll only use wake_up() interface to wake up task.
+However, sometimes, there are epoll users which want to use
+the synchronous wakeup flag to hint the scheduler, such as
+Android binder driver.
+So add a wake_up_sync() define, and use the wake_up_sync()
+when the sync is true in ep_poll_callback().
+
+Co-developed-by: Jing Xia <jing.xia@unisoc.com>
+Signed-off-by: Jing Xia <jing.xia@unisoc.com>
+Signed-off-by: Xuewen Yan <xuewen.yan@unisoc.com>
+Link: https://lore.kernel.org/r/20240426080548.8203-1-xuewen.yan@unisoc.com
+Tested-by: Brian Geffon <bgeffon@google.com>
+Reviewed-by: Brian Geffon <bgeffon@google.com>
+Reported-by: Benoit Lize <lizeb@google.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+[ Redefine wake_up_sync(x) as __wake_up_sync(x, TASK_NORMAL, 1) to
+  make it work on 5.4.y ]
+Signed-off-by: Wenshan Lan <jetlan9@163.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/eventpoll.c       | 5 ++++-
+ include/linux/wait.h | 1 +
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/fs/eventpoll.c b/fs/eventpoll.c
+index 8c0e94183186..569bfff280e4 100644
+--- a/fs/eventpoll.c
++++ b/fs/eventpoll.c
+@@ -1273,7 +1273,10 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v
+                               break;
+                       }
+               }
+-              wake_up(&ep->wq);
++              if (sync)
++                      wake_up_sync(&ep->wq);
++              else
++                      wake_up(&ep->wq);
+       }
+       if (waitqueue_active(&ep->poll_wait))
+               pwake++;
+diff --git a/include/linux/wait.h b/include/linux/wait.h
+index 03bff85e365f..5b65f720261a 100644
+--- a/include/linux/wait.h
++++ b/include/linux/wait.h
+@@ -213,6 +213,7 @@ void __wake_up_pollfree(struct wait_queue_head *wq_head);
+ #define wake_up_all(x)                        __wake_up(x, TASK_NORMAL, 0, NULL)
+ #define wake_up_locked(x)             __wake_up_locked((x), TASK_NORMAL, 1)
+ #define wake_up_all_locked(x)         __wake_up_locked((x), TASK_NORMAL, 0)
++#define wake_up_sync(x)                       __wake_up_sync(x, TASK_NORMAL, 1)
+ #define wake_up_interruptible(x)      __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
+ #define wake_up_interruptible_nr(x, nr)       __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)
+-- 
+2.39.5
+
diff --git a/queue-5.4/ipv6-fix-possible-uaf-in-ip6_finish_output2.patch b/queue-5.4/ipv6-fix-possible-uaf-in-ip6_finish_output2.patch
new file mode 100644 (file)
index 0000000..58dd80c
--- /dev/null
@@ -0,0 +1,52 @@
+From ca7d35c4c81779b3e2e6082693a0334c2876821c 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 a88c3988cbbd..45622cc03e96 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -68,11 +68,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.4/ipv6-use-skb_expand_head-in-ip6_finish_output2.patch b/queue-5.4/ipv6-use-skb_expand_head-in-ip6_finish_output2.patch
new file mode 100644 (file)
index 0000000..923ea94
--- /dev/null
@@ -0,0 +1,130 @@
+From 095395a558920d5580a0fcf7b7575a72ee608892 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 a34a562b0954..906d1be9dee8 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -59,46 +59,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
+@@ -109,7 +92,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);
+@@ -118,9 +101,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;
+@@ -135,10 +116,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);
+@@ -147,7 +128,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.4/ipv6-use-skb_expand_head-in-ip6_xmit.patch b/queue-5.4/ipv6-use-skb_expand_head-in-ip6_xmit.patch
new file mode 100644 (file)
index 0000000..59fa26d
--- /dev/null
@@ -0,0 +1,98 @@
+From 6abb0f254cd836bdbfa74e37a0b0b06b01bda3f0 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 906d1be9dee8..a88c3988cbbd 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -253,6 +253,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;
+@@ -260,22 +262,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) {
+@@ -317,8 +313,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
+@@ -331,17 +326,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.4/mips-probe-toolchain-support-of-msym32.patch b/queue-5.4/mips-probe-toolchain-support-of-msym32.patch
new file mode 100644 (file)
index 0000000..369e76a
--- /dev/null
@@ -0,0 +1,38 @@
+From 0babe92b25709ee488ea5677f5fe042e167ca6d7 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 9ff2c70763a0..e2a2e5df4fde 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 1561d52cb6df3055c6aa48074077f83f2560fd6e..5ebc0bf7756796d669b1e5a348dcf79eb0f6eda9 100644 (file)
@@ -48,3 +48,9 @@ regmap-use-correct-format-specifier-for-logging-rang.patch
 platform-x86-asus-nb-wmi-ignore-unknown-event-0xcf.patch
 scsi-mpt3sas-diag-reset-when-doorbell-in-use-bit-is-.patch
 virtio-blk-don-t-keep-queue-frozen-during-system-sus.patch
+epoll-add-synchronous-wakeup-support-for-ep_poll_cal.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
diff --git a/queue-5.4/skbuff-introduce-skb_expand_head.patch b/queue-5.4/skbuff-introduce-skb_expand_head.patch
new file mode 100644 (file)
index 0000000..190920c
--- /dev/null
@@ -0,0 +1,94 @@
+From ffffe7a329f6a9b5e60bc7da945a9ba19d4b0376 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 3191d0ffc6e9..4edce28fb454 100644
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -1118,6 +1118,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 82be36c87eb6..115c7265c7d6 100644
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -1726,6 +1726,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
+