On 6/23/20, Martin Pieuchot <m...@openbsd.org> wrote: > On 23/06/20(Tue) 01:00, Jason A. Donenfeld wrote: >> You can crash a system by running something like: >> >> for i in 1 2 3; do while true; do ifconfig bridge0 create& ifconfig >> bridge0 destroy& done& done >> >> This works with every type of interface I've tried. It appears that >> if_clone_destroy and if_clone_create race with other ioctls, which >> causes a variety of different UaFs or just general logic errors. > > Thanks for the report. This is a long standing & known issue. > >> One common root cause appears to be that most ifioctl functions use >> ifunit() to find an interface by name, which traverses if_list. Writes >> to if_list are protected by a lock, but reads are apparently >> unprotected. There's also the question of the life time of the object >> returned from ifunit(). Most things that access &ifnet's if_list are >> done without locking, and even if those accesses were to be locked, the >> lock would be released before the object is no longer used, causing the >> UaF in that case as well. > > Any sleeping point between the first ifunit() and the end of `ifc_create' > or `ifc_destroy' respectively can lead to races. > >> This patch fixes the issue by making if_clone_{create,destroy} exclusive >> with all other ifioctls. > > Diff below achieves the same but moves the locking inside the if_clone* > functions such that consumers like tun(4), bridge(4) and switch(4) which > clone interfaces upon open(2) are also serialized. > > I also added a NET_ASSERT_UNLOCKED() in both functions because the new > lock must be grabbed before the NET_LOCK() in order to allow ifc_create > and ifc_destroy to manipulate data structures protected by this lock. > > Comments, ok?
Not okay. This is the first thing I tried, and it still races with ifioctl_get, causing UaF crashes. It's harder to trigger, but still possible after a few minutes of races. This structure here needs to be a read/write lock, which is what my original patch provides. (I guess I forgot to add the "ok?" epilogue to it.) > > Index: net/if.c > =================================================================== > RCS file: /cvs/src/sys/net/if.c,v > retrieving revision 1.610 > diff -u -p -r1.610 if.c > --- net/if.c 22 Jun 2020 09:45:13 -0000 1.610 > +++ net/if.c 23 Jun 2020 10:25:41 -0000 > @@ -224,6 +224,7 @@ void if_idxmap_remove(struct ifnet *); > > TAILQ_HEAD(, ifg_group) ifg_head = TAILQ_HEAD_INITIALIZER(ifg_head); > > +struct rwlock if_clone_lock = RWLOCK_INITIALIZER("if_clone_lock"); > LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners); > int if_cloners_count; > > @@ -1246,24 +1247,36 @@ if_clone_create(const char *name, int rd > struct ifnet *ifp; > int unit, ret; > > - ifc = if_clone_lookup(name, &unit); > - if (ifc == NULL) > - return (EINVAL); > + NET_ASSERT_UNLOCKED(); > + rw_enter_write(&if_clone_lock); > > - if (ifunit(name) != NULL) > - return (EEXIST); > + ifc = if_clone_lookup(name, &unit); > + if (ifc == NULL) { > + ret = EINVAL; > + goto out; > + } > + if (ifunit(name) != NULL) { > + ret = EEXIST; > + goto out; > + } > > ret = (*ifc->ifc_create)(ifc, unit); > + if (ret != 0) > + goto out; > > - if (ret != 0 || (ifp = ifunit(name)) == NULL) > - return (ret); > + ifp = ifunit(name); > + if (ifp == NULL) { > + ret = EAGAIN; > + goto out; > + } > > NET_LOCK(); > if_addgroup(ifp, ifc->ifc_name); > if (rdomain != 0) > if_setrdomain(ifp, rdomain); > NET_UNLOCK(); > - > +out: > + rw_exit_write(&if_clone_lock); > return (ret); > } > > @@ -1277,16 +1290,23 @@ if_clone_destroy(const char *name) > struct ifnet *ifp; > int ret; > > - ifc = if_clone_lookup(name, NULL); > - if (ifc == NULL) > - return (EINVAL); > + NET_ASSERT_UNLOCKED(); > + rw_enter_write(&if_clone_lock); > > + ifc = if_clone_lookup(name, NULL); > + if (ifc == NULL) { > + ret = EINVAL; > + goto out; > + } > ifp = ifunit(name); > - if (ifp == NULL) > - return (ENXIO); > - > - if (ifc->ifc_destroy == NULL) > - return (EOPNOTSUPP); > + if (ifp == NULL) { > + ret = ENXIO; > + goto out; > + } > + if (ifc->ifc_destroy == NULL) { > + ret = EOPNOTSUPP; > + goto out; > + } > > NET_LOCK(); > if (ifp->if_flags & IFF_UP) { > @@ -1297,7 +1317,8 @@ if_clone_destroy(const char *name) > } > NET_UNLOCK(); > ret = (*ifc->ifc_destroy)(ifp); > - > +out: > + rw_exit_write(&if_clone_lock); > return (ret); > } > >