diff --git a/sys/net/pf/pf_if.c b/sys/net/pf/pf_if.c --- a/sys/net/pf/pf_if.c +++ b/sys/net/pf/pf_if.c @@ -283,6 +283,17 @@ if (ifp->if_dunit == IF_DUNIT_NONE) return; + /* + * Serialize tree mutation with the unlocked RB walk in + * pfi_get_ifaces()/pfi_set_flags()/pfi_clear_flags(), which run + * under pf_token (held by pfioctl). Without this, an + * ifnet_attach/detach_event firing on a remote CPU can + * RB_INSERT/RB_REMOVE/free a kif that the walker has already + * captured in its nextp pointer, causing a use-after-free read + * (and potentially a panic when the stale pointer is later + * dereferenced). See DF-0605. + */ + lwkt_gettoken(&pf_token); crit_enter(); pfi_update++; if ((kif = pfi_kif_get(ifp->if_xname)) == NULL) @@ -291,6 +302,7 @@ ifp->if_pf_kif = kif; pfi_kif_update(kif); crit_exit(); + lwkt_reltoken(&pf_token); } void @@ -301,6 +313,8 @@ if ((kif = (struct pfi_kif *)ifp->if_pf_kif) == NULL) return; + /* See comment in pfi_attach_ifnet(). */ + lwkt_gettoken(&pf_token); crit_enter(); pfi_update++; pfi_kif_update(kif); @@ -308,6 +322,7 @@ ifp->if_pf_kif = NULL; pfi_kif_unref(kif, PFI_KIF_REF_NONE); crit_exit(); + lwkt_reltoken(&pf_token); } void @@ -315,6 +330,8 @@ { struct pfi_kif *kif; + /* See comment in pfi_attach_ifnet(). */ + lwkt_gettoken(&pf_token); crit_enter(); pfi_update++; if ((kif = pfi_kif_get(ifg->ifg_group)) == NULL) @@ -322,6 +339,7 @@ kif->pfik_group = ifg; ifg->ifg_pf_kif = kif; crit_exit(); + lwkt_reltoken(&pf_token); } void @@ -332,12 +350,15 @@ if ((kif = (struct pfi_kif *)ifg->ifg_pf_kif) == NULL) return; + /* See comment in pfi_attach_ifnet(). */ + lwkt_gettoken(&pf_token); crit_enter(); pfi_update++; kif->pfik_group = NULL; ifg->ifg_pf_kif = NULL; pfi_kif_unref(kif, PFI_KIF_REF_NONE); crit_exit(); + lwkt_reltoken(&pf_token); } void @@ -345,12 +366,15 @@ { struct pfi_kif *kif; + /* See comment in pfi_attach_ifnet(). */ + lwkt_gettoken(&pf_token); crit_enter(); pfi_update++; if ((kif = pfi_kif_get(group)) == NULL) panic("%s: pfi_kif_get failed", __func__); pfi_kif_update(kif); crit_exit(); + lwkt_reltoken(&pf_token); } int @@ -766,6 +790,16 @@ struct pfi_kif *p, *nextp; int n = 0; + /* + * Hold pf_token across the RB walk so concurrent + * pfi_attach_ifnet/pfi_detach_ifnet/pfi_attach_ifgroup/ + * pfi_detach_ifgroup/pfi_group_change event handlers (which also + * take pf_token) cannot RB_INSERT/RB_REMOVE/free the kif that + * 'nextp' already points at. Recursive-safe: pfioctl() already + * holds pf_token around DIOCIGETIFACES, but the lock is needed + * here in case a future caller forgets. See DF-0605. + */ + lwkt_gettoken(&pf_token); crit_enter(); for (p = RB_MIN(pfi_ifhead, &pfi_ifs); p; p = nextp) { nextp = RB_NEXT(pfi_ifhead, &pfi_ifs, p); @@ -778,6 +812,7 @@ if (copyout(p, buf++, sizeof(*buf))) { pfi_kif_unref(p, PFI_KIF_REF_RULE); crit_exit(); + lwkt_reltoken(&pf_token); return (EFAULT); } nextp = RB_NEXT(pfi_ifhead, &pfi_ifs, p); @@ -785,6 +820,7 @@ } } crit_exit(); + lwkt_reltoken(&pf_token); *size = n; return (0); } @@ -822,6 +858,8 @@ { struct pfi_kif *p; + /* See DF-0605: serialize RB walk with concurrent tree mutators. */ + lwkt_gettoken(&pf_token); crit_enter(); RB_FOREACH(p, pfi_ifhead, &pfi_ifs) { if (pfi_skip_if(name, p)) @@ -829,6 +867,7 @@ p->pfik_flags |= flags; } crit_exit(); + lwkt_reltoken(&pf_token); return (0); } @@ -837,6 +876,8 @@ { struct pfi_kif *p; + /* See DF-0605: serialize RB walk with concurrent tree mutators. */ + lwkt_gettoken(&pf_token); crit_enter(); RB_FOREACH(p, pfi_ifhead, &pfi_ifs) { if (pfi_skip_if(name, p)) @@ -844,6 +885,7 @@ p->pfik_flags &= ~flags; } crit_exit(); + lwkt_reltoken(&pf_token); return (0); }