From 07b969caa939051f167b84a3804bde7dcba4da50 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sat, 28 Dec 2024 18:39:31 -0500 Subject: [PATCH] Fixes for 5.4 Signed-off-by: Sasha Levin --- ...onous-wakeup-support-for-ep_poll_cal.patch | 64 +++++++++ ...x-possible-uaf-in-ip6_finish_output2.patch | 52 +++++++ ...kb_expand_head-in-ip6_finish_output2.patch | 130 ++++++++++++++++++ ...ipv6-use-skb_expand_head-in-ip6_xmit.patch | 98 +++++++++++++ ...ps-probe-toolchain-support-of-msym32.patch | 38 +++++ queue-5.4/series | 6 + .../skbuff-introduce-skb_expand_head.patch | 94 +++++++++++++ 7 files changed, 482 insertions(+) create mode 100644 queue-5.4/epoll-add-synchronous-wakeup-support-for-ep_poll_cal.patch create mode 100644 queue-5.4/ipv6-fix-possible-uaf-in-ip6_finish_output2.patch create mode 100644 queue-5.4/ipv6-use-skb_expand_head-in-ip6_finish_output2.patch create mode 100644 queue-5.4/ipv6-use-skb_expand_head-in-ip6_xmit.patch create mode 100644 queue-5.4/mips-probe-toolchain-support-of-msym32.patch create mode 100644 queue-5.4/skbuff-introduce-skb_expand_head.patch 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 index 00000000000..d403dc8778a --- /dev/null +++ b/queue-5.4/epoll-add-synchronous-wakeup-support-for-ep_poll_cal.patch @@ -0,0 +1,64 @@ +From f8a05f73700a36801f76c3deaede7dcf06f537c5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Dec 2024 19:14:29 +0800 +Subject: epoll: Add synchronous wakeup support for ep_poll_callback + +From: Xuewen Yan + +[ 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 +Signed-off-by: Jing Xia +Signed-off-by: Xuewen Yan +Link: https://lore.kernel.org/r/20240426080548.8203-1-xuewen.yan@unisoc.com +Tested-by: Brian Geffon +Reviewed-by: Brian Geffon +Reported-by: Benoit Lize +Signed-off-by: Christian Brauner +[ 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 +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..58dd80c736e --- /dev/null +++ b/queue-5.4/ipv6-fix-possible-uaf-in-ip6_finish_output2.patch @@ -0,0 +1,52 @@ +From ca7d35c4c81779b3e2e6082693a0334c2876821c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 24 Dec 2024 21:16:24 -0800 +Subject: ipv6: fix possible UAF in ip6_finish_output2() + +From: Eric Dumazet + +[ 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 +Cc: Vasily Averin +Reviewed-by: David Ahern +Link: https://patch.msgid.link/20240820160859.3786976-3-edumazet@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +(cherry picked from commit e891b36de161fcd96f12ff83667473e5067b9037) +Signed-off-by: Harshvardhan Jha +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..923ea9470d7 --- /dev/null +++ b/queue-5.4/ipv6-use-skb_expand_head-in-ip6_finish_output2.patch @@ -0,0 +1,130 @@ +From 095395a558920d5580a0fcf7b7575a72ee608892 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 24 Dec 2024 21:16:22 -0800 +Subject: ipv6: use skb_expand_head in ip6_finish_output2 + +From: Vasily Averin + +[ 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 +Signed-off-by: David S. Miller +(cherry picked from commit e415ed3a4b8b246ee5e9d109ff5153efcf96b9f2) +Signed-off-by: Harshvardhan Jha +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..59fa26ddd22 --- /dev/null +++ b/queue-5.4/ipv6-use-skb_expand_head-in-ip6_xmit.patch @@ -0,0 +1,98 @@ +From 6abb0f254cd836bdbfa74e37a0b0b06b01bda3f0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 24 Dec 2024 21:16:23 -0800 +Subject: ipv6: use skb_expand_head in ip6_xmit + +From: Vasily Averin + +[ 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 +Signed-off-by: David S. Miller +(cherry picked from commit 0c9f227bee11910a49e1d159abe102d06e3745d5) +Signed-off-by: Harshvardhan Jha +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..369e76a7afc --- /dev/null +++ b/queue-5.4/mips-probe-toolchain-support-of-msym32.patch @@ -0,0 +1,38 @@ +From 0babe92b25709ee488ea5677f5fe042e167ca6d7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 24 Dec 2024 14:09:18 +0800 +Subject: MIPS: Probe toolchain support of -msym32 + +From: Jiaxun Yang + +[ 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 +Signed-off-by: Thomas Bogendoerfer +Signed-off-by: WangYuli +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-5.4/series b/queue-5.4/series index 1561d52cb6d..5ebc0bf7756 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -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 index 00000000000..190920c990b --- /dev/null +++ b/queue-5.4/skbuff-introduce-skb_expand_head.patch @@ -0,0 +1,94 @@ +From ffffe7a329f6a9b5e60bc7da945a9ba19d4b0376 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 24 Dec 2024 21:16:21 -0800 +Subject: skbuff: introduce skb_expand_head() + +From: Vasily Averin + +[ 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 +Signed-off-by: David S. Miller +(cherry picked from commit f1260ff15a71b8fc122b2c9abd8a7abffb6e0168) +Signed-off-by: Harshvardhan Jha +Signed-off-by: Sasha Levin +--- + 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 + -- 2.47.3