* [BUG] netfilter: ipv6: nf_defrag: u8 prev_nhoff truncation in find_prev_fhdr()
@ 2026-10-19 10:16 Dominik Maier
2026-10-20 23:05 ` Pablo Neira Ayuso
0 siblings, 1 reply; 2+ messages in thread
From: Dominik Maier @ 2026-10-19 10:16 UTC (permalink / raw)
To: Pablo Neira Ayuso, Florian Westphal; +Cc: netfilter-devel, dmnk+artist
In Linux 7.3.0 (commit c03c1a14507d), find_prev_fhdr() in
net/ipv6/netfilter/nf_conntrack_reasm.c (line 398) tracks the current
extension header offset in int start and writes the preceding header's
nexthdr offset to int *prevhoff, but declares prev_nhoff as u8.
When nf_defrag_ipv6 (CONFIG_NF_DEFRAG_IPV6=y/m) reassembles an incoming
fragmented IPv6 packet whose unfragmentable extension headers place the
header preceding the Fragment header at offset start >= 256 (for
example, start = 296 with a 40-byte IPv6 header, a 256-byte Hop-by-Hop
Options header, and an 8-byte Destination Options header at offset
296..304), assigning prev_nhoff = start truncates 296 modulo 256 to 40.
During reassembly in nf_ct_frag6_reasm(), writing
skb_network_header(head)[nhoff] = fhdr->nexthdr overwrites offset 40
(HopOpts.nexthdr) with IPPROTO_UDP (17) while leaving NEXTHDR_FRAGMENT
at offset 296 (DstOpts.nexthdr). Subsequent Netfilter hooks and
xfrm6_policy_check() (__skb_flow_dissect()) parse the 8-byte
Destination Options header as the outer UDP header, whereas
ip6_protocol_deliver_rcu() delivers the real UDP payload at
skb_transport_offset(skb) = 304 to the destination socket.
We reproduced this in QEMU by sending two IPv6 fragments containing a
256-byte Hop-by-Hop Options header and an 8-byte Destination Options
header (placing the Fragment header at offset 304), causing
nf_defrag_ipv6 to overwrite offset 40 instead of offset 296 and bypass
an inbound XFRM_POLICY_BLOCK policy on the target UDP port. The
reproducer is available upon request.
Below is a proposed fix (also tested in QEMU) changing prev_nhoff from
u8 to int to match start and *prevhoff.
Fixes: 029f7f3b8701 ("netfilter: ipv6: nf_defrag: avoid/free clone operations")
Assisted-by: ARTIST LLM
---
net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c
b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 13bd8997f36c..8fb7380f239d 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -398,7 +398,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp,
int *prevhoff, int *fhoff)
{
u8 nexthdr = ipv6_hdr(skb)->nexthdr;
const int netoff = skb_network_offset(skb);
- u8 prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr);
+ int prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr);
int start = netoff + sizeof(struct ipv6hdr);
int len = skb->len - start;
u8 prevhdr = NEXTHDR_IPV6;
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [BUG] netfilter: ipv6: nf_defrag: u8 prev_nhoff truncation in find_prev_fhdr()
2026-10-19 10:16 [BUG] netfilter: ipv6: nf_defrag: u8 prev_nhoff truncation in find_prev_fhdr() Dominik Maier
@ 2026-10-20 23:05 ` Pablo Neira Ayuso
0 siblings, 0 replies; 2+ messages in thread
From: Pablo Neira Ayuso @ 2026-10-20 23:05 UTC (permalink / raw)
To: Dominik Maier; +Cc: Florian Westphal, netfilter-devel, dmnk+artist
On Mon, Oct 19, 2026 at 05:16:32AM -0500, Dominik Maier wrote:
> In Linux 7.3.0 (commit c03c1a14507d), find_prev_fhdr() in
> net/ipv6/netfilter/nf_conntrack_reasm.c (line 398) tracks the current
> extension header offset in int start and writes the preceding header's
> nexthdr offset to int *prevhoff, but declares prev_nhoff as u8.
>
> When nf_defrag_ipv6 (CONFIG_NF_DEFRAG_IPV6=y/m) reassembles an incoming
> fragmented IPv6 packet whose unfragmentable extension headers place the
> header preceding the Fragment header at offset start >= 256 (for
> example, start = 296 with a 40-byte IPv6 header, a 256-byte Hop-by-Hop
> Options header, and an 8-byte Destination Options header at offset
> 296..304), assigning prev_nhoff = start truncates 296 modulo 256 to 40.
>
> During reassembly in nf_ct_frag6_reasm(), writing
> skb_network_header(head)[nhoff] = fhdr->nexthdr overwrites offset 40
> (HopOpts.nexthdr) with IPPROTO_UDP (17) while leaving NEXTHDR_FRAGMENT
> at offset 296 (DstOpts.nexthdr). Subsequent Netfilter hooks and
> xfrm6_policy_check() (__skb_flow_dissect()) parse the 8-byte
> Destination Options header as the outer UDP header, whereas
> ip6_protocol_deliver_rcu() delivers the real UDP payload at
> skb_transport_offset(skb) = 304 to the destination socket.
>
> We reproduced this in QEMU by sending two IPv6 fragments containing a
> 256-byte Hop-by-Hop Options header and an 8-byte Destination Options
> header (placing the Fragment header at offset 304), causing
> nf_defrag_ipv6 to overwrite offset 40 instead of offset 296 and bypass
> an inbound XFRM_POLICY_BLOCK policy on the target UDP port. The
> reproducer is available upon request.
>
> Below is a proposed fix (also tested in QEMU) changing prev_nhoff from
> u8 to int to match start and *prevhoff.
Dup, for the record:
https://patchwork.ozlabs.org/project/netfilter-devel/patch/20260822214012.1028305-2-Jeremy.Jean@oss.cyber.gouv.fr/
> Fixes: 029f7f3b8701 ("netfilter: ipv6: nf_defrag: avoid/free clone operations")
> Assisted-by: ARTIST LLM
> ---
> net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c
> b/net/ipv6/netfilter/nf_conntrack_reasm.c
> index 13bd8997f36c..8fb7380f239d 100644
> --- a/net/ipv6/netfilter/nf_conntrack_reasm.c
> +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
> @@ -398,7 +398,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp,
> int *prevhoff, int *fhoff)
> {
> u8 nexthdr = ipv6_hdr(skb)->nexthdr;
> const int netoff = skb_network_offset(skb);
> - u8 prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr);
> + int prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr);
> int start = netoff + sizeof(struct ipv6hdr);
> int len = skb->len - start;
> u8 prevhdr = NEXTHDR_IPV6;
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-20 23:05 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-19 10:16 [BUG] netfilter: ipv6: nf_defrag: u8 prev_nhoff truncation in find_prev_fhdr() Dominik Maier
2026-10-20 23:05 ` Pablo Neira Ayuso
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.