Last change reworking art_walk() introduced a regression leading to a of-by-one in rtable_walk(). You can trigger it by running the route(8) regress test 5:
# cd /usr/src/regress/sbin/route && make rttest 5 # route -T5 -n show Routing tables Internet: Destination Gateway Flags Refs Use Mtu Prio Iface 10.8.1/24 192.0.2.2 UGS 0 0 32768 18 (null) 10.8.1/24 192.0.2.2 GS 0 1 32768 16 (null) See the two dangling pointers? Problem is that rtable_walk_helper() have to keep a reference to the next entry in the multipath chain otherwise we might forget it when purging all the items. Here it should be to use the _LOCKED() variant since the helper will be called inside art_walk() which will grab the mutex. Diff below also correct the KASSERT() when deleting an entry. The refcount *must* be at least 2 at this point: 1 for being in the table and 1 for the caller. This should be committed separately. Comments, ok? Index: net/art.c =================================================================== RCS file: /cvs/src/sys/net/art.c,v retrieving revision 1.20 diff -u -p -r1.20 art.c --- net/art.c 22 Jun 2016 06:32:32 -0000 1.20 +++ net/art.c 1 Jul 2016 10:06:06 -0000 @@ -706,11 +706,11 @@ art_walk_apply(struct art_root *ar, { int error = 0; + KERNEL_ASSERT_LOCKED(); + if ((an != NULL) && (an != next)) { /* this assumes an->an_dst is not used by f */ - KERNEL_UNLOCK(); error = (*f)(an, arg); - KERNEL_LOCK(); } return (error); Index: net/rtable.c =================================================================== RCS file: /cvs/src/sys/net/rtable.c,v retrieving revision 1.48 diff -u -p -r1.48 rtable.c --- net/rtable.c 22 Jun 2016 06:32:32 -0000 1.48 +++ net/rtable.c 1 Jul 2016 10:03:52 -0000 @@ -819,7 +819,7 @@ rtable_delete(unsigned int rtableid, str npaths++; if (npaths > 1) { - KASSERT(rt->rt_refcnt >= 1); + KASSERT(rt->rt_refcnt > 1); SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry, rt_next); @@ -834,7 +834,7 @@ rtable_delete(unsigned int rtableid, str if (art_delete(ar, an, addr, plen) == NULL) return (ESRCH); - KASSERT(rt->rt_refcnt >= 1); + KASSERT(rt->rt_refcnt > 1); SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry, rt_next); art_put(an); @@ -853,16 +853,19 @@ struct rtable_walk_cookie { int rtable_walk_helper(struct art_node *an, void *xrwc) { - struct srp_ref sr; struct rtable_walk_cookie *rwc = xrwc; - struct rtentry *rt; + struct rtentry *rt, *nrt; int error = 0; - SRPL_FOREACH(rt, &sr, &an->an_rtlist, rt_next) { - if ((error = (*rwc->rwc_func)(rt, rwc->rwc_arg, rwc->rwc_rid))) + KERNEL_ASSERT_LOCKED(); + + SRPL_FOREACH_SAFE_LOCKED(rt, &an->an_rtlist, rt_next, nrt) { + KERNEL_UNLOCK(); + error = (*rwc->rwc_func)(rt, rwc->rwc_arg, rwc->rwc_rid); + KERNEL_LOCK(); + if (error) break; } - SRPL_LEAVE(&sr); return (error); }