git: 3545fc68f416 - main - sound: Check for errors from sndbuf_resize()

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Wed, 14 Oct 2026 16:02:17 UTC
The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=3545fc68f416ee6d3379e0a44ed9eb22fefee2ef

commit 3545fc68f416ee6d3379e0a44ed9eb22fefee2ef
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-10-13 15:53:39 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-10-14 16:00:47 +0000

    sound: Check for errors from sndbuf_resize()
    
    Most implementations of CHANNEL_SETBLOCKSIZE and CHANNEL_SETFRAGMENTS
    were not checking this.  This is probably not a big problem: it's
    unlikely for sndbuf_resize() to fail due to the refcount check in this
    case since it's operating on the hardware buffer, which doesn't get
    mmaped.  But, I will add more reasons for it to fail in the next patch,
    so let's be more careful.
    
    This change modifies various CHANNEL_SETBLOCKSIZE implementations to
    check for errors.  In some cases I reordered things to avoid updating
    driver state until after sndbuf_resize() succeeds.
    
    Return 0 from CHANNEL_SETBLOCKSIZE to indicate an error, as it otherwise
    returns the new block size.  We could change the interface to return
    both an error number and a block size, but that would break at least one
    out-of-tree driver and I wish to avoid that here.  Make sure that errors
    from CHANNEL_SETFRAGMENTS and CHANNEL_SETBLOCKSIZE are returned to
    userspace.
    
    Approved by:    so
    Security:       FreeBSD-SA-26:71.sound
    Reviewed by:    christos
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D60553
---
 sys/arm/freescale/imx/imx6_ssi.c  |  3 ++-
 sys/arm/freescale/vybrid/vf_sai.c |  3 ++-
 sys/dev/firewire/fwisound_pcm.c   |  3 ++-
 sys/dev/sound/pci/als4000.c       |  3 ++-
 sys/dev/sound/pci/atiixp.c        |  8 ++++++--
 sys/dev/sound/pci/cmi.c           |  3 ++-
 sys/dev/sound/pci/cs4281.c        | 10 ++++++----
 sys/dev/sound/pci/emu10kx-pcm.c   |  2 +-
 sys/dev/sound/pci/envy24.c        | 16 +++++++++-------
 sys/dev/sound/pci/envy24ht.c      | 16 +++++++++-------
 sys/dev/sound/pci/es137x.c        |  8 ++++++--
 sys/dev/sound/pci/hda/hdaa.c      | 12 ++++++++----
 sys/dev/sound/pci/hdsp-pcm.c      |  7 ++++---
 sys/dev/sound/pci/hdspe-pcm.c     |  8 +++++---
 sys/dev/sound/pci/ich.c           | 23 +++++++++++++----------
 sys/dev/sound/pci/t4dwave.c       |  7 ++++---
 sys/dev/sound/pci/via8233.c       |  8 ++++++--
 sys/dev/sound/pci/via82c686.c     |  3 ++-
 sys/dev/sound/pci/vibes.c         |  3 ++-
 sys/dev/sound/pcm/channel.c       | 37 ++++++++++++++++++++++++++++---------
 sys/dev/sound/usb/uaudio.c        | 14 +++++---------
 sys/dev/sound/usb/uaudio.h        |  2 --
 sys/dev/sound/usb/uaudio_pcm.c    |  7 -------
 23 files changed, 124 insertions(+), 82 deletions(-)

diff --git a/sys/arm/freescale/imx/imx6_ssi.c b/sys/arm/freescale/imx/imx6_ssi.c
index cc6a208f8e8a..c032e6b93841 100644
--- a/sys/arm/freescale/imx/imx6_ssi.c
+++ b/sys/arm/freescale/imx/imx6_ssi.c
@@ -394,7 +394,8 @@ ssichan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 	struct sc_pcminfo *scp = ch->parent;
 	struct sc_info *sc = scp->sc;
 
-	sndbuf_resize(ch->buffer, sc->dma_size / blocksize, blocksize);
+	if (sndbuf_resize(ch->buffer, sc->dma_size / blocksize, blocksize) != 0)
+		return (0);
 
 	setup_dma(scp);
 
diff --git a/sys/arm/freescale/vybrid/vf_sai.c b/sys/arm/freescale/vybrid/vf_sai.c
index 82216ae1731b..fa8eb4da5d3e 100644
--- a/sys/arm/freescale/vybrid/vf_sai.c
+++ b/sys/arm/freescale/vybrid/vf_sai.c
@@ -367,7 +367,8 @@ saichan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 	struct sc_pcminfo *scp = ch->parent;
 	struct sc_info *sc = scp->sc;
 
-	sndbuf_resize(ch->buffer, sc->dma_size / blocksize, blocksize);
+	if (sndbuf_resize(ch->buffer, sc->dma_size / blocksize, blocksize) != 0)
+		return (0);
 
 	sc->period = ch->buffer->blksz;
 	return (sc->period);
diff --git a/sys/dev/firewire/fwisound_pcm.c b/sys/dev/firewire/fwisound_pcm.c
index d9ebff56a283..fff26171bbb8 100644
--- a/sys/dev/firewire/fwisound_pcm.c
+++ b/sys/dev/firewire/fwisound_pcm.c
@@ -113,7 +113,8 @@ fwisound_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
 	if (blksz < 4)
 		blksz = 4;
 
-	sndbuf_resize(chan->buf, chan->buf->bufsize / blksz, blksz);
+	if (sndbuf_resize(chan->buf, chan->buf->bufsize / blksz, blksz) != 0)
+		return (0);
 
 	return (chan->buf->blksz);
 }
diff --git a/sys/dev/sound/pci/als4000.c b/sys/dev/sound/pci/als4000.c
index 92f8c526adfa..84da62a0f76b 100644
--- a/sys/dev/sound/pci/als4000.c
+++ b/sys/dev/sound/pci/als4000.c
@@ -267,7 +267,8 @@ alschan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 	if (blocksize > sc->bufsz / 2) {
 		blocksize = sc->bufsz / 2;
 	}
-	sndbuf_resize(ch->buffer, 2, blocksize);
+	if (sndbuf_resize(ch->buffer, 2, blocksize) != 0)
+		return 0;
 	return blocksize;
 }
 
diff --git a/sys/dev/sound/pci/atiixp.c b/sys/dev/sound/pci/atiixp.c
index 708250a0474a..70deac110b3f 100644
--- a/sys/dev/sound/pci/atiixp.c
+++ b/sys/dev/sound/pci/atiixp.c
@@ -532,6 +532,7 @@ atiixp_chan_setfragments(kobj_t obj, void *data,
 {
 	struct atiixp_chinfo *ch = data;
 	struct atiixp_info *sc = ch->parent;
+	int error;
 
 	blksz &= ATI_IXP_BLK_ALIGN;
 
@@ -555,9 +556,11 @@ atiixp_chan_setfragments(kobj_t obj, void *data,
 
 	if ((ch->buffer->blksz != blksz ||
 	    ch->buffer->blkcnt != blkcnt) &&
-	    sndbuf_resize(ch->buffer, blkcnt, blksz) != 0)
+	    (error = sndbuf_resize(ch->buffer, blkcnt, blksz)) != 0) {
 		device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n",
 		    __func__, blksz, blkcnt);
+		return (error);
+	}
 
 	ch->blksz = ch->buffer->blksz;
 	ch->blkcnt = ch->buffer->blkcnt;
@@ -571,7 +574,8 @@ atiixp_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
 	struct atiixp_chinfo *ch = data;
 	struct atiixp_info *sc = ch->parent;
 
-	atiixp_chan_setfragments(obj, data, blksz, sc->blkcnt);
+	if (atiixp_chan_setfragments(obj, data, blksz, sc->blkcnt) != 0)
+		return (0);
 
 	return (ch->blksz);
 }
diff --git a/sys/dev/sound/pci/cmi.c b/sys/dev/sound/pci/cmi.c
index 7d991c0924dc..ace52d727c6a 100644
--- a/sys/dev/sound/pci/cmi.c
+++ b/sys/dev/sound/pci/cmi.c
@@ -470,7 +470,8 @@ cmichan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 	if (blocksize > sc->bufsz / CMI_INTR_PER_BUFFER) {
 		blocksize = sc->bufsz / CMI_INTR_PER_BUFFER;
 	}
-	sndbuf_resize(ch->buffer, CMI_INTR_PER_BUFFER, blocksize);
+	if (sndbuf_resize(ch->buffer, CMI_INTR_PER_BUFFER, blocksize) != 0)
+		return 0;
 
 	return blocksize;
 }
diff --git a/sys/dev/sound/pci/cs4281.c b/sys/dev/sound/pci/cs4281.c
index a770a517e4f6..83b9f2d69481 100644
--- a/sys/dev/sound/pci/cs4281.c
+++ b/sys/dev/sound/pci/cs4281.c
@@ -346,12 +346,14 @@ cs4281chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
     struct sc_info *sc = ch->parent;
     uint32_t go;
 
-    go = adcdac_go(ch, 0);
-
     /* 2 interrupts are possible and used in buffer (half-empty,empty),
      * hence factor of 2. */
-    ch->blksz = min(blocksize, sc->bufsz / 2);
-    sndbuf_resize(ch->buffer, 2, ch->blksz);
+    blocksize = min(blocksize, sc->bufsz / 2);
+    if (sndbuf_resize(ch->buffer, 2, blocksize) != 0)
+	return 0;
+    ch->blksz = blocksize;
+
+    go = adcdac_go(ch, 0);
     ch->dma_setup = 0;
     adcdac_prog(ch);
     adcdac_go(ch, go);
diff --git a/sys/dev/sound/pci/emu10kx-pcm.c b/sys/dev/sound/pci/emu10kx-pcm.c
index be8c53896ce4..235cab269fbd 100644
--- a/sys/dev/sound/pci/emu10kx-pcm.c
+++ b/sys/dev/sound/pci/emu10kx-pcm.c
@@ -785,7 +785,7 @@ emupchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize)
 	blocksize = EMU_PLAY_BUFSZ / blkcnt;
 
 	if (sndbuf_resize(ch->buffer, blkcnt, blocksize) != 0)
-		return (ch->blksz);
+		return (0);
 
 	mtx_lock(&sc->lock);
 	ch->blksz = blocksize;
diff --git a/sys/dev/sound/pci/envy24.c b/sys/dev/sound/pci/envy24.c
index 67724d8eb303..3f9fb29ddac3 100644
--- a/sys/dev/sound/pci/envy24.c
+++ b/sys/dev/sound/pci/envy24.c
@@ -1714,7 +1714,7 @@ envy24chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 {
 	struct sc_chinfo *ch = data;
 	/* struct sc_info *sc = ch->parent; */
-	uint32_t size, prev;
+	uint32_t blk, size, prev;
         unsigned int bcnt, bsize;
 
 #if(0)
@@ -1729,20 +1729,22 @@ envy24chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 			break;
 	}
 
-	ch->blk = prev / ch->unit;
+	blk = prev / ch->unit;
 	if (ch->dir == PCMDIR_PLAY)
-		ch->blk *= ENVY24_PLAY_BUFUNIT / 4;
+		blk *= ENVY24_PLAY_BUFUNIT / 4;
 	else
-		ch->blk *= ENVY24_REC_BUFUNIT / 4;
+		blk *= ENVY24_REC_BUFUNIT / 4;
 	/* set channel buffer information */
 	/* ch->size = ch->unit * ENVY24_SAMPLE_NUM; */
         if (ch->dir == PCMDIR_PLAY)
-                bsize = ch->blk * 4 / ENVY24_PLAY_BUFUNIT;
+                bsize = blk * 4 / ENVY24_PLAY_BUFUNIT;
         else
-                bsize = ch->blk * 4 / ENVY24_REC_BUFUNIT;
+                bsize = blk * 4 / ENVY24_REC_BUFUNIT;
         bsize *= ch->unit;
         bcnt = ch->size / bsize;
-        sndbuf_resize(ch->buffer, bcnt, bsize);
+        if (sndbuf_resize(ch->buffer, bcnt, bsize) != 0)
+		return 0;
+	ch->blk = blk;
 	/* mtx_unlock(&sc->lock); */
 
 #if(0)
diff --git a/sys/dev/sound/pci/envy24ht.c b/sys/dev/sound/pci/envy24ht.c
index 4ceafd366627..a42426b48ab0 100644
--- a/sys/dev/sound/pci/envy24ht.c
+++ b/sys/dev/sound/pci/envy24ht.c
@@ -1625,7 +1625,7 @@ envy24htchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 {
 	struct sc_chinfo *ch = data;
 	/* struct sc_info *sc = ch->parent; */
-	uint32_t size, prev;
+	uint32_t blk, size, prev;
 	unsigned int bcnt, bsize;
 
 #if(0)
@@ -1640,20 +1640,22 @@ envy24htchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 			break;
 	}
 
-	ch->blk = prev / ch->unit;
+	blk = prev / ch->unit;
 	if (ch->dir == PCMDIR_PLAY)
-		ch->blk *= ENVY24HT_PLAY_BUFUNIT / 4;
+		blk *= ENVY24HT_PLAY_BUFUNIT / 4;
 	else
-		ch->blk *= ENVY24HT_REC_BUFUNIT / 4;
+		blk *= ENVY24HT_REC_BUFUNIT / 4;
         /* set channel buffer information */
         /* ch->size = ch->unit * ENVY24HT_SAMPLE_NUM; */
         if (ch->dir == PCMDIR_PLAY)
-                bsize = ch->blk * 4 / ENVY24HT_PLAY_BUFUNIT;
+                bsize = blk * 4 / ENVY24HT_PLAY_BUFUNIT;
         else
-                bsize = ch->blk * 4 / ENVY24HT_REC_BUFUNIT;
+                bsize = blk * 4 / ENVY24HT_REC_BUFUNIT;
         bsize *= ch->unit;
         bcnt = ch->size / bsize;
-        sndbuf_resize(ch->buffer, bcnt, bsize);
+        if (sndbuf_resize(ch->buffer, bcnt, bsize) != 0)
+		return 0;
+	ch->blk = blk;
 	/* mtx_unlock(&sc->lock); */
 
 #if(0)
diff --git a/sys/dev/sound/pci/es137x.c b/sys/dev/sound/pci/es137x.c
index d2c796127f33..2e875672dbfa 100644
--- a/sys/dev/sound/pci/es137x.c
+++ b/sys/dev/sound/pci/es137x.c
@@ -634,6 +634,7 @@ eschan_setfragments(kobj_t obj, void *data, uint32_t blksz, uint32_t blkcnt)
 {
 	struct es_chinfo *ch = data;
 	struct es_info *es = ch->parent;
+	int error;
 
 	blksz &= ES_BLK_ALIGN;
 
@@ -657,9 +658,11 @@ eschan_setfragments(kobj_t obj, void *data, uint32_t blksz, uint32_t blkcnt)
 
 	if ((ch->buffer->blksz != blksz ||
 	    ch->buffer->blkcnt != blkcnt) &&
-	    sndbuf_resize(ch->buffer, blkcnt, blksz) != 0)
+	    (error = sndbuf_resize(ch->buffer, blkcnt, blksz)) != 0) {
 		device_printf(es->dev, "%s: failed blksz=%u blkcnt=%u\n",
 		    __func__, blksz, blkcnt);
+		return (error);
+	}
 
 	ch->bufsz = ch->buffer->bufsize;
 	ch->blksz = ch->buffer->blksz;
@@ -674,7 +677,8 @@ eschan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
 	struct es_chinfo *ch = data;
 	struct es_info *es = ch->parent;
 
-	eschan_setfragments(obj, data, blksz, es->blkcnt);
+	if (eschan_setfragments(obj, data, blksz, es->blkcnt) != 0)
+		return (0);
 
 	return (ch->blksz);
 }
diff --git a/sys/dev/sound/pci/hda/hdaa.c b/sys/dev/sound/pci/hda/hdaa.c
index 8f0d76669548..7a88bf4a36c4 100644
--- a/sys/dev/sound/pci/hda/hdaa.c
+++ b/sys/dev/sound/pci/hda/hdaa.c
@@ -2080,6 +2080,7 @@ hdaa_channel_setfragments(kobj_t obj, void *data,
 					uint32_t blksz, uint32_t blkcnt)
 {
 	struct hdaa_chan *ch = data;
+	int error;
 
 	blksz -= blksz % lcm(HDA_DMA_ALIGNMENT, ch->b->align);
 
@@ -2101,11 +2102,12 @@ hdaa_channel_setfragments(kobj_t obj, void *data,
 			break;
 	}
 
-	if ((ch->b->blksz != blksz ||
-	    ch->b->blkcnt != blkcnt) &&
-	    sndbuf_resize(ch->b, blkcnt, blksz) != 0)
+	if ((ch->b->blksz != blksz || ch->b->blkcnt != blkcnt) &&
+	    (error = sndbuf_resize(ch->b, blkcnt, blksz)) != 0) {
 		device_printf(ch->devinfo->dev, "%s: failed blksz=%u blkcnt=%u\n",
 		    __func__, blksz, blkcnt);
+		return (error);
+	}
 
 	ch->blksz = ch->b->blksz;
 	ch->blkcnt = ch->b->blkcnt;
@@ -2118,7 +2120,9 @@ hdaa_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz)
 {
 	struct hdaa_chan *ch = data;
 
-	hdaa_channel_setfragments(obj, data, blksz, ch->pdevinfo->chan_blkcnt);
+	if (hdaa_channel_setfragments(obj, data, blksz,
+	    ch->pdevinfo->chan_blkcnt) != 0)
+		return (0);
 
 	return (ch->blksz);
 }
diff --git a/sys/dev/sound/pci/hdsp-pcm.c b/sys/dev/sound/pci/hdsp-pcm.c
index bce251079bb7..27ab21fd6d74 100644
--- a/sys/dev/sound/pci/hdsp-pcm.c
+++ b/sys/dev/sound/pci/hdsp-pcm.c
@@ -943,6 +943,10 @@ hdspchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 		}
 	}
 
+	if (sndbuf_resize(ch->buffer, 2,
+	    (hl->period * AFMT_CHANNEL(ch->format) * sizeof(uint32_t))) != 0)
+		return (0);
+
 	mtx_lock(&sc->lock);
 	sc->ctrl_register &= ~HDSP_LAT_MASK;
 	sc->ctrl_register |= hdsp_encode_latency(hl->n);
@@ -954,9 +958,6 @@ hdspchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 	device_printf(scp->dev, "New period=%d\n", sc->period);
 #endif
 
-	sndbuf_resize(ch->buffer, 2,
-	    (sc->period * AFMT_CHANNEL(ch->format) * sizeof(uint32_t)));
-
 	/* Reset pointer, rewrite frequency (same register) for 9632. */
 	hdsp_write_4(sc, HDSP_RESET_POINTER, 0);
 	if (sc->type == HDSP_9632)
diff --git a/sys/dev/sound/pci/hdspe-pcm.c b/sys/dev/sound/pci/hdspe-pcm.c
index c6b248503dab..1f32909ce0d3 100644
--- a/sys/dev/sound/pci/hdspe-pcm.c
+++ b/sys/dev/sound/pci/hdspe-pcm.c
@@ -941,6 +941,11 @@ hdspechan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 		}
 	}
 
+	if (sndbuf_resize(ch->buffer,
+	    (HDSPE_CHANBUF_SIZE * AFMT_CHANNEL(ch->format)) / (hl->period * 4),
+	    (hl->period * 4)) != 0)
+		return (0);
+
 	mtx_lock(&sc->lock);
 	sc->ctrl_register &= ~HDSPE_LAT_MASK;
 	sc->ctrl_register |= hdspe_encode_latency(hl->n);
@@ -952,9 +957,6 @@ hdspechan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 	device_printf(scp->dev, "New period=%d\n", sc->period);
 #endif
 
-	sndbuf_resize(ch->buffer,
-	    (HDSPE_CHANBUF_SIZE * AFMT_CHANNEL(ch->format)) / (sc->period * 4),
-	    (sc->period * 4));
 end:
 
 	return (ch->buffer->blksz);
diff --git a/sys/dev/sound/pci/ich.c b/sys/dev/sound/pci/ich.c
index c36e107b6e79..819e640a4b6d 100644
--- a/sys/dev/sound/pci/ich.c
+++ b/sys/dev/sound/pci/ich.c
@@ -294,21 +294,23 @@ AC97_DECLARE(ich_ac97);
 /* -------------------------------------------------------------------- */
 /* common routines */
 
-static void
-ich_filldtbl(struct sc_chinfo *ch)
+static int
+ich_filldtbl(struct sc_chinfo *ch, uint32_t blksz)
 {
 	struct sc_info *sc = ch->parent;
 	uint32_t base;
-	int i;
+	int error, i;
 
 	base = ch->buffer->buf_addr;
-	if ((ch->blksz * ch->blkcnt) > ch->buffer->maxsize)
-		ch->blksz = ch->buffer->maxsize / ch->blkcnt;
-	if ((ch->buffer->blksz != ch->blksz ||
+	if (blksz * ch->blkcnt > ch->buffer->maxsize)
+		blksz = ch->buffer->maxsize / ch->blkcnt;
+	if ((ch->buffer->blksz != blksz ||
 	    ch->buffer->blkcnt != ch->blkcnt) &&
-	    sndbuf_resize(ch->buffer, ch->blkcnt, ch->blksz) != 0)
+	    (error = sndbuf_resize(ch->buffer, ch->blkcnt, blksz)) != 0) {
 		device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n",
-		    __func__, ch->blksz, ch->blkcnt);
+		    __func__, blksz, ch->blkcnt);
+		return (error);
+	}
 	ch->blksz = ch->buffer->blksz;
 
 	for (i = 0; i < ICH_DTBL_LENGTH; i++) {
@@ -316,6 +318,7 @@ ich_filldtbl(struct sc_chinfo *ch)
 		ch->dtbl[i].length = ICH_BDC_IOC
 				   | (ch->blksz / ch->parent->sample_size);
 	}
+	return (0);
 }
 
 static int
@@ -496,8 +499,8 @@ ichchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 	if (blocksize < ICH_MIN_BLKSZ)
 		blocksize = ICH_MIN_BLKSZ;
 	blocksize &= ~(ICH_MIN_BLKSZ - 1);
-	ch->blksz = blocksize;
-	ich_filldtbl(ch);
+	if (ich_filldtbl(ch, blocksize) != 0)
+		return (0);
 	ICH_LOCK(sc);
 	ich_wr(sc, ch->regbase + ICH_REG_X_LVI, ch->blkcnt - 1, 1);
 	ICH_UNLOCK(sc);
diff --git a/sys/dev/sound/pci/t4dwave.c b/sys/dev/sound/pci/t4dwave.c
index 12cb8ff14956..a2f87b68eb6d 100644
--- a/sys/dev/sound/pci/t4dwave.c
+++ b/sys/dev/sound/pci/t4dwave.c
@@ -538,7 +538,8 @@ trpchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 {
 	struct tr_chinfo *ch = data;
 
-	sndbuf_resize(ch->buffer, 2, blocksize);
+	if (sndbuf_resize(ch->buffer, 2, blocksize) != 0)
+		return 0;
 	return blocksize;
 }
 
@@ -659,8 +660,8 @@ trrchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 {
 	struct tr_rchinfo *ch = data;
 
-	sndbuf_resize(ch->buffer, 2, blocksize);
-
+	if (sndbuf_resize(ch->buffer, 2, blocksize) != 0)
+		return 0;
 	return blocksize;
 }
 
diff --git a/sys/dev/sound/pci/via8233.c b/sys/dev/sound/pci/via8233.c
index f683a36f0213..a960829cc3ea 100644
--- a/sys/dev/sound/pci/via8233.c
+++ b/sys/dev/sound/pci/via8233.c
@@ -565,6 +565,7 @@ via8233chan_setfragments(kobj_t obj, void *data,
 {
 	struct via_chinfo *ch = data;
 	struct via_info *via = ch->parent;
+	int error;
 
 	blksz &= VIA_BLK_ALIGN;
 
@@ -588,9 +589,11 @@ via8233chan_setfragments(kobj_t obj, void *data,
 
 	if ((ch->buffer->blksz != blksz ||
 	    ch->buffer->blkcnt != blkcnt) &&
-	    sndbuf_resize(ch->buffer, blkcnt, blksz) != 0)
+	    (error = sndbuf_resize(ch->buffer, blkcnt, blksz)) != 0) {
 		device_printf(via->dev, "%s: failed blksz=%u blkcnt=%u\n",
 		    __func__, blksz, blkcnt);
+		return (error);
+	}
 
 	ch->blksz = ch->buffer->blksz;
 	ch->blkcnt = ch->buffer->blkcnt;
@@ -604,7 +607,8 @@ via8233chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
 	struct via_chinfo *ch = data;
 	struct via_info *via = ch->parent;
 
-	via8233chan_setfragments(obj, data, blksz, via->blkcnt);
+	if (via8233chan_setfragments(obj, data, blksz, via->blkcnt) != 0)
+		return (0);
 
 	return (ch->blksz);
 }
diff --git a/sys/dev/sound/pci/via82c686.c b/sys/dev/sound/pci/via82c686.c
index b4681901aa97..1e9a651c657c 100644
--- a/sys/dev/sound/pci/via82c686.c
+++ b/sys/dev/sound/pci/via82c686.c
@@ -328,8 +328,9 @@ viachan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 {
 	struct via_chinfo *ch = data;
 
+	if (sndbuf_resize(ch->buffer, SEGS_PER_CHAN, blocksize) != 0)
+		return 0;
 	ch->blksz = blocksize;
-	sndbuf_resize(ch->buffer, SEGS_PER_CHAN, ch->blksz);
 
 	return ch->blksz;
 }
diff --git a/sys/dev/sound/pci/vibes.c b/sys/dev/sound/pci/vibes.c
index 0b5acf6866ec..ba672fb62cd0 100644
--- a/sys/dev/sound/pci/vibes.c
+++ b/sys/dev/sound/pci/vibes.c
@@ -224,7 +224,8 @@ svchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 
         /* user has requested interrupts every blocksize bytes */
 	RANGE(blocksize, SV_MIN_BLKSZ, sc->bufsz / SV_INTR_PER_BUFFER);
-	sndbuf_resize(ch->buffer, SV_INTR_PER_BUFFER, blocksize);
+	if (sndbuf_resize(ch->buffer, SV_INTR_PER_BUFFER, blocksize) != 0)
+		return 0;
 	DEB(printf("svchan_setblocksize: %d\n", blocksize));
 	return blocksize;
 }
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index 0f06564d6218..24877c319b35 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -1837,7 +1837,6 @@ chn_resizebuf(struct pcm_channel *c, int latency,
 {
 	struct snd_dbuf *b, *bs, *pb;
 	int sblksz, sblkcnt, hblksz, hblkcnt, limit = 0, nsblksz, nsblkcnt;
-	int ret;
 	u_int32_t maxsize;
 
 	CHN_LOCKASSERT(c);
@@ -1909,6 +1908,8 @@ chn_resizebuf(struct pcm_channel *c, int latency,
 			    sndbuf_xbytes(pb->blksz, pb, bs) * 2 : 0;
 		}
 	} else {
+		int error;
+
 		/*
 		 * The byte-rate-scaled cap applies to the secondary buffer
 		 * only.  It exists to absorb userland scheduling latency,
@@ -1942,11 +1943,27 @@ chn_resizebuf(struct pcm_channel *c, int latency,
 		hblksz -= hblksz % b->align;
 
 		CHN_UNLOCK(c);
-		if (chn_usefrags == 0 ||
-		    CHANNEL_SETFRAGMENTS(c->methods, c->devinfo,
-		    hblksz, hblkcnt) != 0)
-			b->blksz = CHANNEL_SETBLOCKSIZE(c->methods,
-			    c->devinfo, hblksz);
+		error = ENOTSUP;
+		if (chn_usefrags) {
+			error = CHANNEL_SETFRAGMENTS(c->methods, c->devinfo,
+			    hblksz, hblkcnt);
+			/* The default implementation returns ENOTSUP. */
+			if (error != 0 && error != ENOTSUP) {
+				CHN_LOCK(c);
+				return error;
+			}
+		}
+		if (error == ENOTSUP) {
+			uint32_t nblksz;
+
+			nblksz = CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo,
+			    hblksz);
+			if (nblksz == 0) {
+				CHN_LOCK(c);
+				return EINVAL;
+			}
+			b->blksz = nblksz;
+		}
 		CHN_LOCK(c);
 
 		if (!CHN_EMPTY(c, children)) {
@@ -1985,11 +2002,13 @@ chn_resizebuf(struct pcm_channel *c, int latency,
 
 	if (bs->blkcnt != sblkcnt || bs->blksz != sblksz ||
 	    bs->bufsize != (sblkcnt * sblksz)) {
-		ret = sndbuf_remalloc(bs, sblkcnt, sblksz);
-		if (ret != 0) {
+		int error;
+
+		error = sndbuf_remalloc(bs, sblkcnt, sblksz);
+		if (error != 0) {
 			device_printf(c->dev, "%s(): Failed: %d %d\n",
 			    __func__, sblkcnt, sblksz);
-			return ret;
+			return error;
 		}
 	}
 
diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c
index e65d2c51bfe3..fc4891aff2ee 100644
--- a/sys/dev/sound/usb/uaudio.c
+++ b/sys/dev/sound/usb/uaudio.c
@@ -2792,16 +2792,12 @@ uaudio_chan_free(struct uaudio_chan *ch)
 int
 uaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize)
 {
-	uint32_t temp = 2 * uaudio_get_buffer_size(ch, ch->set_alt);
-	sndbuf_setup(ch->pcm_buf, ch->buf, temp);
-	return (temp / 2);
-}
+	uint32_t temp;
 
-int
-uaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize,
-    uint32_t blockcount)
-{
-	return (1);
+	temp = 2 * uaudio_get_buffer_size(ch, ch->set_alt);
+	if (sndbuf_setup(ch->pcm_buf, ch->buf, temp) != 0)
+		return (0);
+	return (temp / 2);
 }
 
 int
diff --git a/sys/dev/sound/usb/uaudio.h b/sys/dev/sound/usb/uaudio.h
index 54b31a4e7bd2..406afa5d42eb 100644
--- a/sys/dev/sound/usb/uaudio.h
+++ b/sys/dev/sound/usb/uaudio.h
@@ -45,8 +45,6 @@ extern void	*uaudio_chan_init(struct uaudio_chan *ch, struct snd_dbuf *b,
 extern int	uaudio_chan_free(struct uaudio_chan *ch);
 extern int	uaudio_chan_set_param_blocksize(struct uaudio_chan *ch,
 		    uint32_t blocksize);
-extern int	uaudio_chan_set_param_fragments(struct uaudio_chan *ch,
-		    uint32_t blocksize, uint32_t blockcount);
 extern int	uaudio_chan_set_param_speed(struct uaudio_chan *ch,
 		    uint32_t speed);
 extern int	uaudio_chan_getptr(struct uaudio_chan *ch);
diff --git a/sys/dev/sound/usb/uaudio_pcm.c b/sys/dev/sound/usb/uaudio_pcm.c
index 1317c0c7f41d..30f8b3888f6f 100644
--- a/sys/dev/sound/usb/uaudio_pcm.c
+++ b/sys/dev/sound/usb/uaudio_pcm.c
@@ -71,12 +71,6 @@ ua_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 	return (uaudio_chan_set_param_blocksize(data, blocksize));
 }
 
-static int
-ua_chan_setfragments(kobj_t obj, void *data, uint32_t blocksize, uint32_t blockcount)
-{
-	return (uaudio_chan_set_param_fragments(data, blocksize, blockcount));
-}
-
 static int
 ua_chan_trigger(kobj_t obj, void *data, int go)
 {
@@ -114,7 +108,6 @@ static kobj_method_t ua_chan_methods[] = {
 	KOBJMETHOD(channel_setformat, ua_chan_setformat),
 	KOBJMETHOD(channel_setspeed, ua_chan_setspeed),
 	KOBJMETHOD(channel_setblocksize, ua_chan_setblocksize),
-	KOBJMETHOD(channel_setfragments, ua_chan_setfragments),
 	KOBJMETHOD(channel_trigger, ua_chan_trigger),
 	KOBJMETHOD(channel_getptr, ua_chan_getptr),
 	KOBJMETHOD(channel_getcaps, ua_chan_getcaps),