NetBSD Problem Report #60487

From www@netbsd.org  Thu Jul 23 18:26:57 2026
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
	 client-signature RSA-PSS (2048 bits) client-digest SHA256)
	(Client CN "mail.netbsd.org", Issuer "YR2" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 5099C1A923B
	for <gnats-bugs@gnats.NetBSD.org>; Thu, 23 Jul 2026 18:26:57 +0000 (UTC)
Message-Id: <20260723182655.EA70A1A923C@mollari.NetBSD.org>
Date: Thu, 23 Jul 2026 18:26:55 +0000 (UTC)
From: campbell+netbsd@mumble.net
Reply-To: campbell+netbsd@mumble.net
To: gnats-bugs@NetBSD.org
Subject: netinet6: fragment accounting leak
X-Send-Pr-Version: www-1.0
X-From4GNATS: "campbell+netbsd@mumble.net via gnats" <gnats-admin@NetBSD.org>

>Number:         60487
>Category:       kern
>Synopsis:       netinet6: fragment accounting leak
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Jul 23 18:30:00 +0000 2026
>Last-Modified:  Thu Jul 23 21:10:01 +0000 2026
>Originator:     Taylor R Campbell
>Release:        current, 11, 10, 9, ...
>Organization:
The NetBSDv6 Fragmentation, CPA
>Environment:
>Description:

	When frag6_input enqueues a fragment for reassembly, it
	increments a counter of the number of fragments in flight
	systemwide and for this packet:

    421 insert:
    422 	/*
    423 	 * Stick new segment in its place.
    424 	 */
    425 	frag6_enq(ip6af, af6->ip6af_up);
    426 	frag6_nfrags++;
    427 	q6->ip6q_nfrag++;

https://nxr.netbsd.org/xref/src/sys/netinet6/frag6.c?r=1.78#421

	frag6_nfrags is used to reject an excess of fragments:

    246 	/*
    247 	 * Enforce upper bound on number of fragments.
    248 	 * If maxfrag is 0, never accept fragments.
    249 	 * If maxfrag is -1, accept all fragments without limitation.
    250 	 */
    251 	if (ip6_maxfrags < 0)
    252 		;
    253 	else if (frag6_nfrags >= (u_int)ip6_maxfrags)
    254 		goto dropfrag;

https://nxr.netbsd.org/xref/src/sys/netinet6/frag6.c?r=1.78#246

	And q6->ip6q_nfrag is used to count back down when the packet
	reassembly is completed or aborted:

    123 static void
    124 frag6_dropfrag(struct ip6q *q6)
    125 {
    126 	frag6_remque(q6);
    127 	frag6_nfrags -= q6->ip6q_nfrag;
    128 	kmem_intr_free(q6, sizeof(*q6));
    129 	frag6_nfragpackets--;
    130 }

https://nxr.netbsd.org/xref/src/sys/netinet6/frag6.c?r=1.78#123

	However, there is a path in fragment input where an existing
	fragment is discarded -- and not deducted from q6->ip6q_nfrag
	or frag6_nfrags:

    346 			if (q6->ip6q_unfrglen + af6->ip6af_off + af6->ip6af_frglen >
    347 			    IPV6_MAXPACKET) {
    348 				struct mbuf *merr = af6->ip6af_m;
    349 				struct ip6_hdr *ip6err;
    350 				int erroff = af6->ip6af_offset;
    351 
    352 				/* dequeue the fragment. */
    353 				frag6_deq(af6);
    354 				kmem_intr_free(af6, sizeof(struct ip6asfrag));

https://nxr.netbsd.org/xref/src/sys/netinet6/frag6.c?r=1.78#346

	Taking this path can lead to fragments being spuriously dropped
	while frag6_nfrags is high even though there aren't that many
	fragments in flight.

	The accounting doesn't go completely wrong, from what I can
	tell: since the dropped fragment is still counted in
	q6->ip6q_nfrag, it will be properly deducted from frag6_nfrags
	when frag6_dropfrag finally runs.

>How-To-Repeat:

	send ongoing carefully crafted fragments

>Fix:

--- src/sys/netinet6/frag6.c
+++ src/sys/netinet6/frag6.c
@@ -350,6 +348,8 @@
 				int erroff = af6->ip6af_offset;

 				/* dequeue the fragment. */
+				q6->ip6q_nfrag--;
+				frag6_nfrags--;
 				frag6_deq(af6);
 				kmem_intr_free(af6, sizeof(struct ip6asfrag));


>Audit-Trail:
From: Taylor R Campbell <riastradh@NetBSD.org>
To: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Cc: 
Subject: Re: kern/60487: netinet6: fragment accounting leak
Date: Thu, 23 Jul 2026 21:09:11 +0000

 Another report forwarded to us describes a similar accounting leak for
 frag6_nfragpackets:

     267 	if (q6 == &ip6q) {
     268 		/*
     269 		 * the first fragment to arrive, create a reassembly queue.
     270 		 */
     271 		first_frag = 1;
 ...
     284 		frag6_nfragpackets++;
     285 
     286 		q6 = kmem_intr_zalloc(sizeof(struct ip6q), KM_NOSLEEP);
     287 		if (q6 == NULL) {
     288 			goto dropfrag;
     289 		}
 ...
     301 	}
 ...
     313 	/*
     314 	 * Check that the reassembled packet would not exceed 65535 bytes
     315 	 * in size. If it would exceed, discard the fragment and return an
     316 	 * ICMP error.
     317 	 */
     318 	if (q6->ip6q_unfrglen >= 0) {
     319 		/* The 1st fragment has already arrived. */
     320 		if (q6->ip6q_unfrglen + fragoff + frgpartlen > IPV6_MAXPACKET) {
     321 			mutex_exit(&frag6_lock);
     322 			icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
     323 			    offset - sizeof(struct ip6_frag) +
     324 			    offsetof(struct ip6_frag, ip6f_offlg));
     325 			goto done;
     326 		}
     327 	} else if (fragoff + frgpartlen > IPV6_MAXPACKET) {
     328 		mutex_exit(&frag6_lock);
     329 		icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
     330 		    offset - sizeof(struct ip6_frag) +
     331 		    offsetof(struct ip6_frag, ip6f_offlg));
     332 		goto done;
     333 	}

 https://nxr.netbsd.org/xref/src/sys/netinet6/frag6.c?r=1.79#267

 In these error branches, the claim is that frag6_nfragpackets leaks.
 So perhaps we should have

 +	if (first_frag)
 +		frag6_dropfrag(q6);
  	goto done;

 in these branches.

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.