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.
(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.