DragonFlyBSD Kernel Audit
DF-0306 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/net/ip_mroute/ip_mroute.c b/sys/net/ip_mroute/ip_mroute.c
--- a/sys/net/ip_mroute/ip_mroute.c
+++ b/sys/net/ip_mroute/ip_mroute.c
@@ -2298,10 +2298,21 @@
 	    return 0;		/* XXX Already installed */
 	}
     }
-    lwkt_reltoken(&mroute_token);
+    /*
+     * Keep the token held across the blocking kmalloc below; the old
+     * code released it here and re-acquired after allocation, but
+     * del_mfc()/MRT_DONE could free `mfc` during the gap, turning the
+     * subsequent writes through `mfc` into a use-after-free.
+     * M_INTWAIT allocation is permitted under a token since tokens are
+     * not held across blocking calls that could re-enter this code path.
+     */
 
     /* Allocate the new bw_meter entry */
-    x = kmalloc(sizeof(*x), M_BWMETER, M_INTWAIT);
+    x = kmalloc(sizeof(*x), M_BWMETER, M_INTWAIT | M_NULLOK);
+    if (x == NULL) {
+	lwkt_reltoken(&mroute_token);
+	return ENOBUFS;
+    }
 
     /* Set the new bw_meter entry */
     x->bm_threshold.b_time = req->bu_threshold.b_time;
@@ -2315,8 +2326,10 @@
     x->bm_time_next = NULL;
     x->bm_time_hash = BW_METER_BUCKETS;
 
-    /* Add the new bw_meter entry to the front of entries for this MFC */
-    lwkt_gettoken(&mroute_token);
+    /*
+     * mfc is still valid: the token was held continuously since
+     * mfc_find(), so del_mfc() could not have freed it.
+     */
     x->bm_mfc = mfc;
     x->bm_mfc_next = mfc->mfc_bw_meter;
     mfc->mfc_bw_meter = x;