⬢ DragonFlyBSD Kernel Audit
DF-4558 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/nata/atapi-cam.c
+++ b/sys/dev/disk/nata/atapi-cam.c
@@ -913,22 +913,40 @@
     int devcount;
 
     switch (what) {
-	case MOD_UNLOAD:
+	case MOD_UNLOAD: {
+	    int busy = 0;
+
 	    if (devclass_get_devices(atapi_cam_devclass, &devlist, &devcount)
 		  != 0)
 		return ENXIO;
 	    if (devlist != NULL) {
-		while (devlist != NULL && devcount > 0) {
+		while (devcount > 0) {
 		    device_t child = devlist[--devcount];
 		    struct atapi_xpt_softc *scp = device_get_softc(child);
 
-		    device_delete_child(device_get_parent(child),child);
-		    if (scp != NULL)
+		    if (device_delete_child(device_get_parent(child),child) != 0) {
+			busy = 1;
+			continue;
+		    }
+		    /*
+		     * The softc is external (device_set_softc in identify),
+		     * so we own it.  Only free it when the SIM is gone:
+		     * free_softc() leaves BUS_REGISTERED set (and the SIM
+		     * live in the XPT) when the bus could not be
+		     * deregistered - freeing the softc then would leave the
+		     * still-reachable cam_sim with a dangling softc.
+		     */
+		    if (scp != NULL && (scp->flags & BUS_REGISTERED) == 0)
 			kfree(scp, M_ATACAM);
+		    else
+			busy = 1;
 		}
 		kfree(devlist, M_TEMP);
 	    }
+	    if (busy != 0)
+		return EBUSY;	/* stay resident instead of UAF */
 	    break;
+	}
 
 	default:
 	    break;