Re: mbuf cluster m_extref_mtx contention

Alexander Bluhm <[email protected]>
Newsgroups gmane.os.openbsd.tech
Message-ID <[email protected]>
On Wed, Apr 22, 2026 at 04:21:42PM +1000, David Gwynne wrote:
> from another perspective, if the use after free is the result
> mishandling mbufs, then the other diff is more forgiving of a buggy
> behaviour.

After fixing my refcount btrace use-after-free bug, I don't see
crashes with this diff anymore.  It improves performance.  It should
be commited.

OK bluhm@

> Index: kern/uipc_mbuf.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/uipc_mbuf.c,v
> diff -u -p -r1.304 uipc_mbuf.c
> --- kern/uipc_mbuf.c	5 Feb 2026 03:26:00 -0000	1.304
> +++ kern/uipc_mbuf.c	22 Apr 2026 06:04:19 -0000
> @@ -123,9 +123,21 @@ int max_linkhdr;		/* largest link-level 
>  int max_protohdr;		/* largest protocol header */
>  int max_hdr;			/* largest link+protocol header */
>  
> -struct	mutex m_extref_mtx = MUTEX_INITIALIZER(IPL_NET);
> +struct m_ext_refs {
> +	void		*arg;
> +	u_int		 free_fn;
> +	u_int		 zero;
> +	struct refcnt	 refs;
> +};
> +
> +static struct pool	m_ext_refs_pool;
> +
> +static void		m_extfree_refs(caddr_t, u_int, void *);
> +u_int			m_extfree_refs_fn;
> +
> +static int		m_extref(struct mbuf *, struct mbuf *, int);
> +static void		m_extfree(struct mbuf *);
>  
> -void	m_extfree(struct mbuf *);
>  void	m_zero(struct mbuf *);
>  
>  unsigned long mbuf_mem_limit;	/* [a] how much memory can be allocated */
> @@ -174,6 +186,8 @@ mbinit(void)
>  
>  	pool_init(&mtagpool, PACKET_TAG_MAXSIZE + sizeof(struct m_tag), 0,
>  	    IPL_NET, 0, "mtagpl", NULL);
> +	pool_init(&m_ext_refs_pool, sizeof(struct m_ext_refs), CACHELINESIZE,
> +	    IPL_NET, 0, "mextrefs", NULL);
>  
>  	for (i = 0; i < nitems(mclsizes); i++) {
>  		lowbits = mclsizes[i] & ((1 << 10) - 1);
> @@ -193,6 +207,7 @@ mbinit(void)
>  
>  	(void)mextfree_register(m_extfree_pool);
>  	KASSERT(num_extfree_fns == 1);
> +	m_extfree_refs_fn = mextfree_register(m_extfree_refs);
>  }
>  
>  void
> @@ -204,6 +219,7 @@ mbcpuinit(void)
>  
>  	pool_cache_init(&mbpool);
>  	pool_cache_init(&mtagpool);
> +	pool_cache_init(&m_ext_refs_pool);
>  
>  	for (i = 0; i < nitems(mclsizes); i++)
>  		pool_cache_init(&mclpools[i]);
> @@ -399,6 +415,32 @@ m_extfree_pool(caddr_t buf, u_int size, 
>  	pool_put(pp, buf);
>  }
>  
> +int
> +m_ext_refs_shared(struct mbuf *m)
> +{
> +	struct m_ext_refs *mrefs = m->m_ext.ext_arg;
> +
> +	return (refcnt_shared(&mrefs->refs));
> +}
> +
> +static void
> +m_extfree_refs(caddr_t buf, u_int size, void *arg)
> +{
> +	struct m_ext_refs *mrefs = arg;
> +
> +	if (refcnt_rele(&mrefs->refs)) {
> +		if (mrefs->zero)
> +			explicit_bzero(buf, size);
> +
> +		KASSERT(mrefs->free_fn < num_extfree_fns);
> +		KASSERT(mrefs->free_fn != m_extfree_refs_fn);
> +
> +		mextfree_fns[mrefs->free_fn](buf, size, mrefs->arg);
> +
> +		pool_put(&m_ext_refs_pool, mrefs);
> +	}
> +}
> +
>  struct mbuf *
>  m_free(struct mbuf *m)
>  {
> @@ -434,44 +476,33 @@ m_free(struct mbuf *m)
>  	return (n);
>  }
>  
> -void
> -m_extref(struct mbuf *o, struct mbuf *n)
> +static int
> +m_extref(struct mbuf *m, struct mbuf *n, int how)
>  {
> -	int refs = MCLISREFERENCED(o);
> +	struct m_ext_refs *mrefs;
>  
> -	n->m_flags |= o->m_flags & (M_EXT|M_EXTWR);
> +	if (m->m_ext.ext_free_fn == m_extfree_refs_fn)
> +		mrefs = m->m_ext.ext_arg;
> +	else {
> +		mrefs = pool_get(&m_ext_refs_pool, how);
> +		if (mrefs == NULL)
> +			return (ENOMEM);
>  
> -	if (refs)
> -		mtx_enter(&m_extref_mtx);
> -	n->m_ext.ext_nextref = o->m_ext.ext_nextref;
> -	n->m_ext.ext_prevref = o;
> -	o->m_ext.ext_nextref = n;
> -	n->m_ext.ext_nextref->m_ext.ext_prevref = n;
> -	if (refs)
> -		mtx_leave(&m_extref_mtx);
> -
> -	MCLREFDEBUGN((n), __FILE__, __LINE__);
> -}
> +		refcnt_init(&mrefs->refs);
> +		mrefs->arg = m->m_ext.ext_arg;
> +		mrefs->free_fn = m->m_ext.ext_free_fn;
> +		mrefs->zero = 0;
>  
> -static inline u_int
> -m_extunref(struct mbuf *m)
> -{
> -	int refs = 0;
> +		m->m_ext.ext_arg = mrefs;
> +		m->m_ext.ext_free_fn = m_extfree_refs_fn;
> +	}
>  
> -	if (!MCLISREFERENCED(m))
> -		return (0);
> +	refcnt_take(&mrefs->refs);
>  
> -	mtx_enter(&m_extref_mtx);
> -	if (MCLISREFERENCED(m)) {
> -		m->m_ext.ext_nextref->m_ext.ext_prevref =
> -		    m->m_ext.ext_prevref;
> -		m->m_ext.ext_prevref->m_ext.ext_nextref =
> -		    m->m_ext.ext_nextref;
> -		refs = 1;
> -	}
> -	mtx_leave(&m_extref_mtx);
> +	MEXTADD(n, m->m_ext.ext_buf, m->m_ext.ext_size,
> +	    m->m_flags & M_EXTWR, m_extfree_refs_fn, mrefs);
>  
> -	return (refs);
> +	return (0);
>  }
>  
>  /*
> @@ -487,15 +518,13 @@ mextfree_register(void (*fn)(caddr_t, u_
>  	return num_extfree_fns++;
>  }
>  
> -void
> +static void
>  m_extfree(struct mbuf *m)
>  {
> -	if (m_extunref(m) == 0) {
> -		KASSERT(m->m_ext.ext_free_fn < num_extfree_fns);
> -		mextfree_fns[m->m_ext.ext_free_fn](m->m_ext.ext_buf,
> -		    m->m_ext.ext_size, m->m_ext.ext_arg);
> -	}
> -
> +	KASSERT(m->m_ext.ext_free_fn < num_extfree_fns);
> +	mextfree_fns[m->m_ext.ext_free_fn](m->m_ext.ext_buf,
> +	    m->m_ext.ext_size, m->m_ext.ext_arg);
> + 
>  	m->m_flags &= ~(M_EXT|M_EXTWR);
>  }
>  
> @@ -656,9 +685,9 @@ m_copym(struct mbuf *m0, int off, int le
>  		}
>  		n->m_len = min(len, m->m_len - off);
>  		if (m->m_flags & M_EXT) {
> +			if (m_extref(m, n, wait) != 0)
> +				goto nospace;
>  			n->m_data = m->m_data + off;
> -			n->m_ext = m->m_ext;
> -			MCLADDREFERENCE(m, n);
>  		} else {
>  			n->m_data += m->m_data -
>  			    (m->m_flags & M_PKTHDR ? m->m_pktdat : m->m_dat);
> @@ -1089,8 +1118,12 @@ m_split(struct mbuf *m0, int len0, int w
>  			return (NULL);
>  	}
>  	if (m->m_flags & M_EXT) {
> -		n->m_ext = m->m_ext;
> -		MCLADDREFERENCE(m, n);
> +		if (m_extref(m, n, wait) != 0) {
> +			m_freem(n);
> +			if (m0->m_flags & M_PKTHDR)
> +				m0->m_pkthdr.len = olen;
> +			return (NULL);
> +		}
>  		n->m_data = m->m_data + len;
>  	} else {
>  		m_align(n, remain);
> @@ -1271,13 +1304,17 @@ m_devget(char *buf, int totlen, int off)
>  void
>  m_zero(struct mbuf *m)
>  {
> -	if (M_READONLY(m)) {
> -		mtx_enter(&m_extref_mtx);
> -		if ((m->m_flags & M_EXT) && MCLISREFERENCED(m)) {
> -			m->m_ext.ext_nextref->m_flags |= M_ZEROIZE;
> -			m->m_ext.ext_prevref->m_flags |= M_ZEROIZE;
> -		}
> -		mtx_leave(&m_extref_mtx);
> +	if (ISSET(m->m_flags, M_EXT) &&
> +	    m->m_ext.ext_free_fn == m_extfree_refs_fn) {
> +		struct m_ext_refs *mrefs = m->m_ext.ext_arg;
> +
> +		/*
> +		 * this variable only transitions in one direction,
> +		 * so if there is a race it will be toward the same
> +		 * result and therefore there is no loss.
> +		 */
> +
> +		mrefs->zero = 1;
>  		return;
>  	}
>  
> @@ -1525,9 +1562,7 @@ m_print(void *v,
>  		    m->m_ext.ext_buf, m->m_ext.ext_size);
>  		(*pr)("m_ext.ext_free_fn: %u\tm_ext.ext_arg: %p\n",
>  		    m->m_ext.ext_free_fn, m->m_ext.ext_arg);
> -		(*pr)("m_ext.ext_nextref: %p\tm_ext.ext_prevref: %p\n",
> -		    m->m_ext.ext_nextref, m->m_ext.ext_prevref);
> -
> +		/* if m_ext.ext_free_fn == m_extfree_refs_fn ? */
>  	}
>  }
>  
> Index: sys/mbuf.h
> ===================================================================
> RCS file: /cvs/src/sys/sys/mbuf.h,v
> diff -u -p -r1.269 mbuf.h
> --- sys/mbuf.h	5 Feb 2026 03:26:00 -0000	1.269
> +++ sys/mbuf.h	22 Apr 2026 06:04:19 -0000
> @@ -145,8 +145,6 @@ struct mbuf_ext {
>  	void	*ext_arg;
>  	u_int	ext_free_fn;		/* index of free function */
>  	u_int	ext_size;		/* size of buffer, for ext_free_fn */
> -	struct mbuf *ext_nextref;
> -	struct mbuf *ext_prevref;
>  #ifdef DEBUG
>  	const char *ext_ofile;
>  	const char *ext_nfile;
> @@ -282,13 +280,22 @@ struct mbuf {
>  #define MCLREFDEBUGO(m, file, line)
>  #endif
>  
> -#define	MCLISREFERENCED(m)	((m)->m_ext.ext_nextref != (m))
> +int	m_ext_refs_shared(struct mbuf *);
>  
> -#define	MCLADDREFERENCE(o, n)	m_extref((o), (n))
> +static inline int
> +m_extreferenced(struct mbuf *m)
> +{
> +	extern u_int m_extfree_refs_fn;
> +
> +	if (m->m_ext.ext_free_fn != m_extfree_refs_fn)
> +		return (0);
> +
> +	return (m_ext_refs_shared(m));
> +}
> +
> +#define	MCLISREFERENCED(m)	m_extreferenced(m)
>  
>  #define	MCLINITREFERENCE(m)	do {					\
> -		(m)->m_ext.ext_prevref = (m);				\
> -		(m)->m_ext.ext_nextref = (m);				\
>  		MCLREFDEBUGO((m), __FILE__, __LINE__);			\
>  		MCLREFDEBUGN((m), NULL, 0);				\
>  	} while (/* CONSTCOND */ 0)
> @@ -439,7 +446,6 @@ int	m_leadingspace(struct mbuf *);
>  int	m_trailingspace(struct mbuf *);
>  void	m_align(struct mbuf *, int);
>  struct mbuf *m_clget(struct mbuf *, int, u_int);
> -void	m_extref(struct mbuf *, struct mbuf *);
>  void	m_pool_init(struct pool *, u_int, u_int, const char *);
>  u_int	m_pool_used(void);
>  void	m_extfree_pool(caddr_t, u_int, void *);
> 
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.