All of lore.kernel.org
 help / color / mirror / Atom feed
* [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.