git: f4d953bea740 - main - bhyve: Do not panic on invalid input in HDA emulation

Christos Margiolis <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a899acb.337c3.5cf69274__25557.9528909706$1787402984$gmane$org@gitrepo.freebsd.org>
The branch main has been updated by christos:

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

commit f4d953bea74091874f019e3a2c4902dd013ee739
Author:     Christos Margiolis <[email protected]>
AuthorDate: 2026-08-22 12:48:32 +0000
Commit:     Christos Margiolis <[email protected]>
CommitDate: 2026-08-22 12:48:32 +0000

    bhyve: Do not panic on invalid input in HDA emulation
    
    The emulated HDA controller passed values taken from guest registers and
    from guest memory straight into assert(), so a guest could abort bhyve
    with values the emulation did not expect. Reject them instead.
    
    In case the guest asked to start something and it failed, clear the
    corresponding run/enable bit.
    
    PR:             256379, 256381, 256382, 256383, 256384, 256385, 256386, 256498
    Sponsored by:   The FreeBSD Foundation
    MFC after:      2 weeks
    Reviewed by:    bnovkov, jhb
    Differential Revision:  https://reviews.freebsd.org/D59082
---
 usr.sbin/bhyve/pci_hda.c | 55 ++++++++++++++++++++++++++++++++----------------
 1 file changed, 37 insertions(+), 18 deletions(-)

diff --git a/usr.sbin/bhyve/pci_hda.c b/usr.sbin/bhyve/pci_hda.c
index 97d6264413e0..92e347f641d9 100644
--- a/usr.sbin/bhyve/pci_hda.c
+++ b/usr.sbin/bhyve/pci_hda.c
@@ -612,14 +612,20 @@ hda_stream_start(struct hda_softc *sc, uint8_t stream_ind)
 	uint8_t strm = 0;
 	uint8_t dir = 0;
 
-	assert(!st->run);
+	if (st->run) {
+		DPRINTF("Stream 0x%x is already running", stream_ind);
+		return (-1);
+	}
 
 	lvi = hda_get_reg_by_offset(sc, off + HDAC_SDLVI);
 	bdpl = hda_get_reg_by_offset(sc, off + HDAC_SDBDPL);
 	bdpu = hda_get_reg_by_offset(sc, off + HDAC_SDBDPU);
 
+	if (lvi >= HDA_BDL_MAX_LEN) {
+		DPRINTF("Invalid LVI: 0x%x", lvi);
+		return (-1);
+	}
 	bdl_cnt = lvi + 1;
-	assert(bdl_cnt <= HDA_BDL_MAX_LEN);
 
 	bdl_paddr = bdpl | (bdpu << 32);
 	bdl_vaddr = hda_dma_get_vaddr(sc, bdl_paddr,
@@ -637,7 +643,10 @@ hda_stream_start(struct hda_softc *sc, uint8_t stream_ind)
 	bdle = (struct hda_bdle *)bdl_vaddr;
 	for (size_t i = 0; i < bdl_cnt; i++, bdle++) {
 		bdle_sz = bdle->len;
-		assert(!(bdle_sz % HDA_DMA_ACCESS_LEN));
+		if ((bdle_sz % HDA_DMA_ACCESS_LEN) != 0) {
+			DPRINTF("Invalid BDLE length: 0x%x", bdle_sz);
+			return (-1);
+		}
 
 		bdle_addrl = bdle->addrl;
 		bdle_addrh = bdle->addrh;
@@ -785,7 +794,6 @@ hda_corb_run(struct hda_softc *sc)
 {
 	struct hda_codec_cmd_ctl *corb = &sc->corb;
 	uint32_t verb = 0;
-	int err;
 
 	corb->wp = hda_get_reg_by_offset(sc, HDAC_CORBWP);
 	if (corb->wp >= corb->size) {
@@ -801,8 +809,8 @@ hda_corb_run(struct hda_softc *sc)
 		verb = hda_dma_ld_dword((uint8_t *)corb->dma_vaddr +
 		    HDA_CORB_ENTRY_LEN * corb->rp);
 
-		err = hda_send_command(sc, verb);
-		assert(!err);
+		if (hda_send_command(sc, verb) != 0)
+			DPRINTF("Fail to send verb: 0x%x", verb);
 	}
 
 	hda_set_reg_by_offset(sc, HDAC_CORBRP, corb->rp);
@@ -928,13 +936,16 @@ static void
 hda_set_corbctl(struct hda_softc *sc, uint32_t offset, uint32_t old)
 {
 	uint32_t value = hda_get_reg_by_offset(sc, offset);
-	int err;
 	struct hda_codec_cmd_ctl *corb = NULL;
 
 	if (value & HDAC_CORBCTL_CORBRUN) {
 		if (!(old & HDAC_CORBCTL_CORBRUN)) {
-			err = hda_corb_start(sc);
-			assert(!err);
+			if (hda_corb_start(sc) != 0) {
+				DPRINTF("Fail to start CORB");
+				hda_set_field_by_offset(sc, offset,
+				    HDAC_CORBCTL_CORBRUN, 0);
+				return;
+			}
 		}
 	} else {
 		corb = &sc->corb;
@@ -948,12 +959,15 @@ static void
 hda_set_rirbctl(struct hda_softc *sc, uint32_t offset, uint32_t old __unused)
 {
 	uint32_t value = hda_get_reg_by_offset(sc, offset);
-	int err;
 	struct hda_codec_cmd_ctl *rirb = NULL;
 
 	if (value & HDAC_RIRBCTL_RIRBDMAEN) {
-		err = hda_rirb_start(sc);
-		assert(!err);
+		if (hda_rirb_start(sc) != 0) {
+			DPRINTF("Fail to start RIRB");
+			hda_set_field_by_offset(sc, offset,
+			    HDAC_RIRBCTL_RIRBDMAEN, 0);
+			return;
+		}
 	} else {
 		rirb = &sc->rirb;
 		memset(rirb, 0, sizeof(*rirb));
@@ -996,7 +1010,8 @@ hda_set_dpiblbase(struct hda_softc *sc, uint32_t offset, uint32_t old)
 			if (!sc->dma_pib_vaddr) {
 				DPRINTF("Fail to get the guest \
 					 virtual address");
-				assert(0);
+				hda_set_field_by_offset(sc, offset,
+				    HDAC_DPLBASE_DPLBASE_DMAPBE, 0);
 			}
 		} else {
 			DPRINTF("DMA Position In Buffer Reset");
@@ -1010,7 +1025,6 @@ hda_set_sdctl(struct hda_softc *sc, uint32_t offset, uint32_t old)
 {
 	uint8_t stream_ind = hda_get_stream_by_offsets(offset, HDAC_SDCTL0);
 	uint32_t value = hda_get_reg_by_offset(sc, offset);
-	int err;
 
 	DPRINTF("stream_ind: 0x%x old: 0x%x value: 0x%x",
 	    stream_ind, old, value);
@@ -1021,11 +1035,16 @@ hda_set_sdctl(struct hda_softc *sc, uint32_t offset, uint32_t old)
 
 	if ((value & HDAC_SDCTL_RUN) != (old & HDAC_SDCTL_RUN)) {
 		if (value & HDAC_SDCTL_RUN) {
-			err = hda_stream_start(sc, stream_ind);
-			assert(!err);
+			if (hda_stream_start(sc, stream_ind) != 0) {
+				DPRINTF("Fail to start stream 0x%x",
+				    stream_ind);
+				hda_set_field_by_offset(sc, offset,
+				    HDAC_SDCTL_RUN, 0);
+			}
 		} else {
-			err = hda_stream_stop(sc, stream_ind);
-			assert(!err);
+			if (hda_stream_stop(sc, stream_ind) != 0)
+				DPRINTF("Fail to stop stream 0x%x",
+				    stream_ind);
 		}
 	}
 }
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.