NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/60487: netinet6: fragment accounting leak



>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
>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));
 




Home | Main Index | Thread Index | Old Index