diff --git a/sys/net/if_clone.c b/sys/net/if_clone.c --- a/sys/net/if_clone.c +++ b/sys/net/if_clone.c @@ -144,11 +144,6 @@ int len, maxclone; int unit; - LIST_FOREACH(ifct, &if_cloners, ifc_list) { - if (strcmp(ifct->ifc_name, ifc->ifc_name) == 0) - return (EEXIST); - } - KASSERT(ifc->ifc_minifs - 1 <= ifc->ifc_maxunit, ("%s: %s requested more units then allowed (%d > %d)", __func__, ifc->ifc_name, ifc->ifc_minifs, @@ -163,10 +158,25 @@ ifc->ifc_units = kmalloc(len, M_CLONE, M_WAITOK | M_ZERO); ifc->ifc_bmlen = len; + /* + * Serialize all list/count mutations and lookups with ifnet_lock so + * that an unlocked reader in if_clone_list()/if_clone_lookup() cannot + * race a detach (which frees ifc->ifc_units and is followed by module + * unload unmapping the ifc backing store). See DF-0726. + */ + ifnet_lock(); + LIST_FOREACH(ifct, &if_cloners, ifc_list) { + if (strcmp(ifct->ifc_name, ifc->ifc_name) == 0) { + ifnet_unlock(); + kfree(ifc->ifc_units, M_CLONE); + ifc->ifc_units = NULL; + return (EEXIST); + } + } + LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list); if_cloners_count++; - ifnet_lock(); for (unit = 0; unit < ifc->ifc_minifs; unit++) { if_clone_alloc_unit(ifc, &unit); if (if_clone_createif(ifc, unit, NULL, NULL) != 0) { @@ -189,9 +199,18 @@ if_clone_detach(struct if_clone *ifc) { + /* + * Serialize against if_clone_list()/if_clone_lookup() readers; without + * this, a reader can hold a pointer into the module's backing store + * across the LIST_REMOVE + kfree(ifc_units) below and the subsequent + * module unload that unmaps the ifc page. See DF-0726. + */ + ifnet_lock(); LIST_REMOVE(ifc, ifc_list); kfree(ifc->ifc_units, M_CLONE); + ifc->ifc_units = NULL; if_cloners_count--; + ifnet_unlock(); } /* @@ -204,14 +223,24 @@ struct if_clone *ifc; int count, error = 0; + /* + * if_cloners / if_cloners_count are mutated by if_clone_attach() and + * if_clone_detach() (module load/unload); take ifnet_lock to serialize + * against them so the LIST_FOREACH cannot chase a freed/unmapped ifc. + * See DF-0726. + */ + ifnet_lock(); ifcr->ifcr_total = if_cloners_count; if ((dst = ifcr->ifcr_buffer) == NULL) { /* Just asking how many there are. */ + ifnet_unlock(); return (0); } - if (ifcr->ifcr_count < 0) + if (ifcr->ifcr_count < 0) { + ifnet_unlock(); return (EINVAL); + } count = (if_cloners_count < ifcr->ifcr_count) ? if_cloners_count : ifcr->ifcr_count; @@ -225,6 +254,7 @@ if (error) break; } + ifnet_unlock(); return (error); } @@ -297,10 +327,20 @@ { struct if_clone *ifc; + /* + * Serialize against if_clone_attach()/if_clone_detach() writers; the + * LIST_FOREACH otherwise races module unload which frees the ifc backing + * store. See DF-0726. Callers (if_clone_create, if_clone_destroy) do + * NOT hold ifnet_lock at this point, so acquiring it here is safe. + */ + ifnet_lock(); LIST_FOREACH(ifc, &if_cloners, ifc_list) { - if (if_clone_match(ifc, name)) + if (if_clone_match(ifc, name)) { + ifnet_unlock(); return ifc; + } } + ifnet_unlock(); return (NULL); }