⬢ DragonFlyBSD Kernel Audit
DF-4223 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/virtual/vmware/pvscsi/pvscsi.c
+++ b/sys/dev/virtual/vmware/pvscsi/pvscsi.c
@@ -201,6 +201,8 @@
 	struct pvscsi_ring_cmp_desc	*cmp_ring;
 	struct pvscsi_ring_msg_desc	*msg_ring;
 	uint32_t		 hcb_cnt;
+	/* number of hcbs whose per-hcb DMA resources exist */
+	uint32_t		 hcbs_setup;
 	struct pvscsi_hcb	*hcbs;
 	SLIST_HEAD(, pvscsi_hcb)	free_list;
 	bus_dma_tag_t		parent_dmat;
@@ -611,10 +613,15 @@
 		hcb = sc->hcbs + i;
 		SLIST_INSERT_HEAD(&sc->free_list, hcb, links);
 	}
+	sc->hcbs_setup = sc->hcb_cnt;
 
 fail:
 	if (error) {
+		/* per-hcb resources for [0..i) were just freed above; zero
+		 * hcbs_setup so pvscsi_free_all() does not destroy those
+		 * maps again (double kfree in bus_dmamap_destroy) */
 		pvscsi_dma_free_per_hcb(sc, i);
+		sc->hcbs_setup = 0;
 	}
 
 	return (error);
@@ -1583,7 +1590,9 @@
 		cam_sim_free(sc->sim);
 	}
 
-	pvscsi_dma_free_per_hcb(sc, sc->hcb_cnt);
+	/* when sc->hcbs == NULL (hcb array alloc failed) hcbs_setup is 0, so
+	 * we never callout_drain()/bus_dmamap_destroy() through a NULL base */
+	pvscsi_dma_free_per_hcb(sc, sc->hcbs_setup);
 
 	if (sc->hcbs) {
 		kfree(sc->hcbs, M_PVSCSI);