Fix for DF-4026: mmap-vs-chn_resizebuf use-after-free. dsp_mmap_single() maps bs->buf under the channel lock; chn_resizebuf() checks CHN_F_MMAP only at entry and then drops the lock (parent-notify window, SETBLOCKSIZE window, sndbuf_remalloc kmalloc window) before freeing the old buffer. A mapper that acquires the lock inside one of those windows gets a mapping of a buffer freed moments later. Two complementary guards: (1) chn_resizebuf() re-checks CHN_F_MMAP after the last relock, right before sndbuf_remalloc(); (2) sndbuf_remalloc() re-checks CHN_F_MMAP after its kmalloc-window relock and keeps the old (possibly mapped) buffers if a mapper appeared. --- a/sys/dev/sound/pcm/channel.c 2026-09-17 16:38:58.624936172 +0000 +++ b/sys/dev/sound/pcm/channel.c 2026-09-17 16:38:58.648935866 +0000 @@ -1905,6 +1905,15 @@ sblksz -= sblksz % sndbuf_getalign(bs); + /* + * DF-4026: the lock was dropped above (parent notify and + * SETBLOCKSIZE windows); a concurrent dsp_mmap_single() may + * have mapped the current buffers in the meantime. Never + * free buffers that are mapped. + */ + if (c->flags & CHN_F_MMAP) + return (EINVAL); + if (sndbuf_getblkcnt(bs) != sblkcnt || sndbuf_getblksz(bs) != sblksz || sndbuf_getsize(bs) != (sblkcnt * sblksz)) { ret = sndbuf_remalloc(bs, sblkcnt, sblksz); --- a/sys/dev/sound/pcm/buffer.c 2026-09-17 16:38:58.624936172 +0000 +++ b/sys/dev/sound/pcm/buffer.c 2026-09-17 16:38:58.660935713 +0000 @@ -226,6 +226,18 @@ tmpbuf = kmalloc(allocsize, M_DEVBUF, M_WAITOK); shadbuf = kmalloc(allocsize, M_DEVBUF, M_WAITOK); CHN_LOCK(b->channel); + /* + * DF-4026: a concurrent dsp_mmap_single() may have + * acquired the channel lock while we were allocating + * and mapped the *current* (old) buffers. Do not free + * them in that case; discard the new allocations. + */ + if (b->channel->flags & CHN_F_MMAP) { + kfree(buf, M_DEVBUF); + kfree(tmpbuf, M_DEVBUF); + kfree(shadbuf, M_DEVBUF); + return (EINVAL); + } if (b->buf != NULL) kfree(b->buf, M_DEVBUF); b->buf = buf;