NetBSD Problem Report #60985

From www@netbsd.org  Mon Nov  9 10:08:05 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 "YR1" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 865EB1A923B
	for <gnats-bugs@gnats.NetBSD.org>; Mon,  9 Nov 2026 10:08:05 +0000 (UTC)
Message-Id: <20261109100803.BB1B31A923C@mollari.NetBSD.org>
Date: Mon,  9 Nov 2026 10:08:03 +0000 (UTC)
From: campbell+netbsd@mumble.net
Reply-To: campbell+netbsd@mumble.net
To: gnats-bugs@NetBSD.org
Subject: m_makewritable woes
X-Send-Pr-Version: www-1.0
X-From4GNATS: "campbell+netbsd@mumble.net via gnats" <gnats-admin@NetBSD.org>

>Number:         60985
>Category:       kern
>Synopsis:       m_makewritable woes
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Nov 09 10:10:00 +0000 2026
>Last-Modified:  Tue Nov 10 02:15:01 +0000 2026
>Originator:     Taylor R Campbell
>Release:        current, 11, 10, ...
>Organization:
The m_writers' Guild, Inc.
>Environment:
>Description:

	Summary:

	1. m_makewritable(9) doesn't make the result contiguous
	2. various callers assume it does
	3. not clear whether m_pullup(9) makes the result writable
	4. if m_pullup and m_makewritable must be combined, not clear
	   in which order


	m_makewritable(9) was added in 2004 to support some
	copy-on-write semantics, presumably as a performance
	optimization for some mbuf operations so that parts of mbuf
	chains can be shared in copies.

	https://mail-index.netbsd.org/source-changes/2004/09/06/msg152408.html

	It is explicitly documented _not_ to make the specified range
	contiguous:

     m_makewritable(struct mbuf **mp, int off, int len, int how)
              Rearranges an mbuf chain so that len bytes from offset off are
              writable.  When it meets read-only mbufs, it allocates new
              mbufs, adjusts the chain as m_copyback_cow() does, and copies
              the original content into them.  m_makewritable() does not
              guarantee that all len bytes at off are consecutive.  [...]

	This was added by dyoung@ in 2006 with a commit message that
	makes me suspect he had recently been bitten by this:

> Let there be no mistake, m_makewritable() does not guarantee that
> the writable bytes [off, off+len) are consecutive.

	https://mail-index.netbsd.org/source-changes/2006/07/28/msg205537.html

	(However, I didn't see any recent commits by dyoung@ that
	explain exactly what might have bitten him.)

	In contrast, m_pullup(9) is guaranteed to make the first len
	bytes contiguous:

     m_pullup(struct mbuf *m, int len)
              Rearranges an mbuf chain so that len bytes are contiguous and in
              the data area of an mbuf (so that mtod() will work for a
              structure of size len).  [...]

	But it's not clear whether m_pullup is guaranteed to make those
	first len bytes writable.

	We have various callers of m_makewritable in tree that use mtod
	immediately afterward:

    209 	int error = m_makewritable(mp, 0, M_COPYALL, M_DONTWAIT);
...
    217 	ip = mtod(*mp, struct ip *);

	https://nxr.netbsd.org/xref/src/sys/external/bsd/ipf/netinet/ip_fil_netbsd.c?r=1.40#203

   1896 	if (m->m_len < sizeof(*evl) &&
   1897 	    (m = m_pullup(m, sizeof(*evl))) == NULL) {
...
   1899 	}
   1900 
   1901 	if (m_makewritable(&m, 0, sizeof(*evl), M_DONTWAIT)) {
...
   1904 	}
   1905 
   1906 	evl = mtod(m, struct ether_vlan_header *);

	https://nxr.netbsd.org/xref/src/sys/net/if_ethersubr.c?r=1.336#1896

    245 	const int tlen = off + len;
...
    254 	if (m_makewritable(&m, 0, tlen, M_NOWAIT)) {
...
    262 	}
    263 	nbuf->nb_mbuf = m;
    264 	nbuf->nb_nptr = mtod(m, uint8_t *) + off;

	https://nxr.netbsd.org/xref/src/sys/net/npf/npf_mbuf.c?r=1.25#245

    398 /*
    399  * Insure there is sufficient contiguous space to encapsulate the
    400  * 802.11 data frame.  If room isn't already there, arrange for it.
...
    406  */
    407 static struct mbuf *
    408 ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
    409 	struct ieee80211_key *key, struct mbuf *m)
    410 {
...
    482 	if (wlen != 0 && m_makewritable(&m, 0, wlen, M_DONTWAIT) != 0) {
...
    485 	}
    486 
    487 	return m;

	https://nxr.netbsd.org/xref/src/sys/net80211/ieee80211_output.c?r=1.68#398

    591 	m = ieee80211_mbuf_adjust(ic, hdrsize, key, m);
    592 	if (m == NULL) {
...
    595 	}
    596 
    597 	/* NB: this could be optimized because of ieee80211_mbuf_adjust */
    598 	m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
    599 	llc = mtod(m, struct llc *);

	https://nxr.netbsd.org/xref/src/sys/net80211/ieee80211_output.c?r=1.68#591

	(I don't think m_adj(m, len) is guaranteed to make anything
	contiguous either.)

    797 	if (__predict_false(m_makewritable(&m, 0, m->m_pkthdr.len, M_DONTWAIT)))
    798 		goto out;
    799 	ah = mtod(m, struct arphdr *);

	https://nxr.netbsd.org/xref/src/sys/netinet/if_arp.c?r=1.321#797

	^ This part was the subject of trouble after fixes for:
	PR kern/60491: arp creates state for invalid messages
	https://gnats.NetBSD.org/60491

    391 	if (__predict_false(
    392 	    m_makewritable(&m, 0, sizeof(struct ip6_hdr), M_DONTWAIT))) {
    393 		IP6_STATINC(IP6_STAT_IDROPPED);
    394 		goto bad;
    395 	}
    396 	ip6 = mtod(m, struct ip6_hdr *);

	https://nxr.netbsd.org/xref/src/sys/netinet6/ip6_input.c?r=1.228#391

>How-To-Repeat:

	lift up a rock and watch the bugs scurry around

>Fix:

	1. Determine whether it is reasonable for m_makewritable to not
	   make the result contiguous.  (There are other callers than
	   the ones quoted above, e.g. in netipsec which don't use
	   mtod; maybe non-contiguous results are fine for those.)

	2. Document whether m_pullup guarantees to make the result
	   readable or preserves the read-only vs read/write aspect of
	   the input.

	3. Decide how to fix the callers that need contiguous results:

	   (a) No change, if m_makewritable is changed to make the
	       result contiguous.

	   (b) Call m_pullup _before_, if that's the right thing.

	   (c) Call m_pullup _after_, if that's the right thing.

>Release-Note:

>Audit-Trail:

From: Robert Elz <kre@munnari.OZ.AU>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: kern/60985: m_makewritable woes
Date: Tue, 10 Nov 2026 09:09:51 +0700

 A fourth possible fix:

 	(d) add a new function m_pullupwritable() which does both at once.

 (which assumes also fixing callers to call that function when required).


>Unformatted:

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.