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;