diff options
author | Martin Pieuchot <mpi@cvs.openbsd.org> | 2019-06-21 17:11:44 +0000 |
---|---|---|
committer | Martin Pieuchot <mpi@cvs.openbsd.org> | 2019-06-21 17:11:44 +0000 |
commit | fb482f64f911b7fca87d82ac917a331783e5915c (patch) | |
tree | 5de16192a2d5c2016a8f06548095348d71e6ebdf /sys/net | |
parent | d54cd84095c832ef5111a5e80e44ec90ec860873 (diff) |
Prevent recursions by not deleting entries inside rtable_walk(9).
rtable_walk(9) now passes a routing entry back to the caller when
a non zero value is returned and if it asked for it.
This allows us to call rtdeletemsg()/rtrequest_delete() from the
caller without creating a recursion because of rtflushclone().
Multicast code hasn't been adapted and is still possibly creating
recursions. However multicast route entries aren't cloned so if
a recursion exists it isn't because of rtflushclone().
Fix stack exhaustion triggered by the use of "-msave-args".
Issue reported by Dániel Lévai on bugs@ confirmed by and ok bluhm@.
Diffstat (limited to 'sys/net')
-rw-r--r-- | sys/net/if_spppsubr.c | 4 | ||||
-rw-r--r-- | sys/net/route.c | 114 | ||||
-rw-r--r-- | sys/net/route.h | 6 | ||||
-rw-r--r-- | sys/net/rtable.c | 16 | ||||
-rw-r--r-- | sys/net/rtable.h | 4 | ||||
-rw-r--r-- | sys/net/rtsock.c | 7 |
6 files changed, 99 insertions, 52 deletions
diff --git a/sys/net/if_spppsubr.c b/sys/net/if_spppsubr.c index c7c283e2480..4627e18ba9b 100644 --- a/sys/net/if_spppsubr.c +++ b/sys/net/if_spppsubr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_spppsubr.c,v 1.174 2018/02/19 08:59:52 mpi Exp $ */ +/* $OpenBSD: if_spppsubr.c,v 1.175 2019/06/21 17:11:42 mpi Exp $ */ /* * Synchronous PPP link level subroutines. * @@ -4167,7 +4167,7 @@ sppp_update_gw(struct ifnet *ifp) /* update routing table */ for (tid = 0; tid <= RT_TABLEID_MAX; tid++) { - rtable_walk(tid, AF_INET, sppp_update_gw_walker, ifp); + rtable_walk(tid, AF_INET, NULL, sppp_update_gw_walker, ifp); } } diff --git a/sys/net/route.c b/sys/net/route.c index 2cde81661a7..b87a31ab789 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -1,4 +1,4 @@ -/* $OpenBSD: route.c,v 1.385 2019/06/13 08:12:10 claudio Exp $ */ +/* $OpenBSD: route.c,v 1.386 2019/06/21 17:11:42 mpi Exp $ */ /* $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $ */ /* @@ -156,7 +156,7 @@ void rt_timer_init(void); int rt_setgwroute(struct rtentry *, u_int); void rt_putgwroute(struct rtentry *); int rtflushclone1(struct rtentry *, void *, u_int); -void rtflushclone(unsigned int, struct rtentry *); +int rtflushclone(struct ifnet *ifp, struct rtentry *, unsigned int); int rt_ifa_purge_walker(struct rtentry *, void *, unsigned int); struct rtentry *rt_match(struct sockaddr *, uint32_t *, int, unsigned int); int rt_clone(struct rtentry **, struct sockaddr *, unsigned int); @@ -702,7 +702,6 @@ rtflushclone1(struct rtentry *rt, void *arg, u_int id) { struct rtentry *cloningrt = arg; struct ifnet *ifp; - int error; if (!ISSET(rt->rt_flags, RTF_CLONED)) return 0; @@ -722,23 +721,35 @@ rtflushclone1(struct rtentry *rt, void *arg, u_int id) if (ifp == NULL) return 0; - error = rtdeletemsg(rt, ifp, id); - if (error == 0) - error = EAGAIN; - if_put(ifp); - return error; + return EEXIST; } -void -rtflushclone(unsigned int rtableid, struct rtentry *parent) +int +rtflushclone(struct ifnet *ifp, struct rtentry *parent, unsigned int rtableid) { + struct rtentry *rt = NULL; + int error; #ifdef DIAGNOSTIC if (!parent || (parent->rt_flags & RTF_CLONING) == 0) panic("rtflushclone: called with a non-cloning route"); #endif - rtable_walk(rtableid, rt_key(parent)->sa_family, rtflushclone1, parent); + + do { + error = rtable_walk(rtableid, rt_key(parent)->sa_family, &rt, + rtflushclone1, parent); + if (rt != NULL && error == EEXIST) { + error = rtdeletemsg(rt, ifp, rtableid); + if (error == 0) + error = EAGAIN; + } + rtfree(rt); + rt = NULL; + } while (error == EAGAIN); + + return error; + } int @@ -780,7 +791,7 @@ rtrequest_delete(struct rt_addrinfo *info, u_int8_t prio, struct ifnet *ifp, /* Clean up any cloned children. */ if (ISSET(rt->rt_flags, RTF_CLONING)) - rtflushclone(tableid, rt); + rtflushclone(ifp, rt, tableid); rtfree(rt->rt_parent); rt->rt_parent = NULL; @@ -1284,12 +1295,13 @@ rt_ifa_dellocal(struct ifaddr *ifa) /* * Remove all addresses attached to ``ifa''. */ -void +int rt_ifa_purge(struct ifaddr *ifa) { struct ifnet *ifp = ifa->ifa_ifp; + struct rtentry *rt = NULL; unsigned int rtableid; - int i; + int error, af = ifa->ifa_addr->sa_family; KASSERT(ifp != NULL); @@ -1297,27 +1309,38 @@ rt_ifa_purge(struct ifaddr *ifa) /* skip rtables that are not in the rdomain of the ifp */ if (rtable_l2(rtableid) != ifp->if_rdomain) continue; - for (i = 1; i <= AF_MAX; i++) { - rtable_walk(rtableid, i, rt_ifa_purge_walker, ifa); - } + + do { + error = rtable_walk(rtableid, af, &rt, + rt_ifa_purge_walker, ifa); + if (rt != NULL && error == EEXIST) { + error = rtdeletemsg(rt, ifp, rtableid); + if (error == 0) + error = EAGAIN; + } + rtfree(rt); + rt = NULL; + } while (error == EAGAIN); + + if (error == EAFNOSUPPORT) + error = 0; + + if (error) + break; } + + return error; } int rt_ifa_purge_walker(struct rtentry *rt, void *vifa, unsigned int rtableid) { struct ifaddr *ifa = vifa; - struct ifnet *ifp = ifa->ifa_ifp; - int error; - - if (rt->rt_ifa != ifa) - return (0); - if ((error = rtdeletemsg(rt, ifp, rtableid))) { - return (error); - } + if (rt->rt_ifa == ifa) + return EEXIST; - return (EAGAIN); + return 0; } /* @@ -1639,23 +1662,42 @@ rtlabel_unref(u_int16_t id) } } -void +int rt_if_track(struct ifnet *ifp) { - int i; - u_int tid; + unsigned int rtableid; + struct rtentry *rt = NULL; + int i, error; - for (tid = 0; tid < rtmap_limit; tid++) { + for (rtableid = 0; rtableid < rtmap_limit; rtableid++) { /* skip rtables that are not in the rdomain of the ifp */ - if (rtable_l2(tid) != ifp->if_rdomain) + if (rtable_l2(rtableid) != ifp->if_rdomain) continue; for (i = 1; i <= AF_MAX; i++) { - if (!rtable_mpath_capable(tid, i)) + if (!rtable_mpath_capable(rtableid, i)) continue; - rtable_walk(tid, i, rt_if_linkstate_change, ifp); + do { + error = rtable_walk(rtableid, i, &rt, + rt_if_linkstate_change, ifp); + if (rt != NULL && error == EEXIST) { + error = rtdeletemsg(rt, ifp, rtableid); + if (error == 0) + error = EAGAIN; + } + rtfree(rt); + rt = NULL; + } while (error == EAGAIN); + + if (error == EAFNOSUPPORT) + error = 0; + + if (error) + break; } } + + return (error); } int @@ -1690,9 +1732,7 @@ rt_if_linkstate_change(struct rtentry *rt, void *arg, u_int id) */ if (ISSET(rt->rt_flags, RTF_CLONED|RTF_DYNAMIC) && !ISSET(rt->rt_flags, RTF_CACHED|RTF_BFD)) { - if ((error = rtdeletemsg(rt, ifp, id))) - return (error); - return (EAGAIN); + return (EEXIST); } if (!ISSET(rt->rt_flags, RTF_UP)) @@ -1823,7 +1863,7 @@ int db_show_arptab(void) { db_printf("Route tree for AF_INET\n"); - rtable_walk(0, AF_INET, db_show_rtentry, NULL); + rtable_walk(0, AF_INET, NULL, db_show_rtentry, NULL); return (0); } #endif /* DDB */ diff --git a/sys/net/route.h b/sys/net/route.h index 93ed426607c..f116002d058 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -1,4 +1,4 @@ -/* $OpenBSD: route.h,v 1.175 2019/04/28 17:59:51 mpi Exp $ */ +/* $OpenBSD: route.h,v 1.176 2019/06/21 17:11:42 mpi Exp $ */ /* $NetBSD: route.h,v 1.9 1996/02/13 22:00:49 christos Exp $ */ /* @@ -451,7 +451,7 @@ void rtfree(struct rtentry *); int rt_ifa_add(struct ifaddr *, int, struct sockaddr *, unsigned int); int rt_ifa_del(struct ifaddr *, int, struct sockaddr *, unsigned int); -void rt_ifa_purge(struct ifaddr *); +int rt_ifa_purge(struct ifaddr *); int rt_ifa_addlocal(struct ifaddr *); int rt_ifa_dellocal(struct ifaddr *); void rtredirect(struct sockaddr *, struct sockaddr *, struct sockaddr *, struct rtentry **, unsigned int); @@ -459,7 +459,7 @@ int rtrequest(int, struct rt_addrinfo *, u_int8_t, struct rtentry **, u_int); int rtrequest_delete(struct rt_addrinfo *, u_int8_t, struct ifnet *, struct rtentry **, u_int); -void rt_if_track(struct ifnet *); +int rt_if_track(struct ifnet *); int rt_if_linkstate_change(struct rtentry *, void *, u_int); int rtdeletemsg(struct rtentry *, struct ifnet *, u_int); #endif /* _KERNEL */ diff --git a/sys/net/rtable.c b/sys/net/rtable.c index 092d53b265f..ec57747f447 100644 --- a/sys/net/rtable.c +++ b/sys/net/rtable.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtable.c,v 1.68 2019/03/05 19:07:56 anton Exp $ */ +/* $OpenBSD: rtable.c,v 1.69 2019/06/21 17:11:42 mpi Exp $ */ /* * Copyright (c) 2014-2016 Martin Pieuchot @@ -664,6 +664,7 @@ leave: struct rtable_walk_cookie { int (*rwc_func)(struct rtentry *, void *, unsigned int); void *rwc_arg; + struct rtentry **rwc_prt; unsigned int rwc_rid; }; @@ -679,16 +680,21 @@ rtable_walk_helper(struct art_node *an, void *xrwc) int error = 0; SRPL_FOREACH(rt, &sr, &an->an_rtlist, rt_next) { - if ((error = (*rwc->rwc_func)(rt, rwc->rwc_arg, rwc->rwc_rid))) + error = (*rwc->rwc_func)(rt, rwc->rwc_arg, rwc->rwc_rid); + if (error != 0) break; } + if (rwc->rwc_prt != NULL && rt != NULL) { + rtref(rt); + *rwc->rwc_prt = rt; + } SRPL_LEAVE(&sr); return (error); } int -rtable_walk(unsigned int rtableid, sa_family_t af, +rtable_walk(unsigned int rtableid, sa_family_t af, struct rtentry **prt, int (*func)(struct rtentry *, void *, unsigned int), void *arg) { struct art_root *ar; @@ -701,10 +707,10 @@ rtable_walk(unsigned int rtableid, sa_family_t af, rwc.rwc_func = func; rwc.rwc_arg = arg; + rwc.rwc_prt = prt; rwc.rwc_rid = rtableid; - while ((error = art_walk(ar, rtable_walk_helper, &rwc)) == EAGAIN) - continue; + error = art_walk(ar, rtable_walk_helper, &rwc); return (error); } diff --git a/sys/net/rtable.h b/sys/net/rtable.h index 69909cf9a55..c492d684739 100644 --- a/sys/net/rtable.h +++ b/sys/net/rtable.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rtable.h,v 1.23 2019/04/28 17:59:51 mpi Exp $ */ +/* $OpenBSD: rtable.h,v 1.24 2019/06/21 17:11:42 mpi Exp $ */ /* * Copyright (c) 2014-2016 Martin Pieuchot @@ -48,7 +48,7 @@ int rtable_insert(unsigned int, struct sockaddr *, struct rtentry *); int rtable_delete(unsigned int, struct sockaddr *, struct sockaddr *, struct rtentry *); -int rtable_walk(unsigned int, sa_family_t, +int rtable_walk(unsigned int, sa_family_t, struct rtentry **, int (*)(struct rtentry *, void *, unsigned int), void *); int rtable_mpath_capable(unsigned int, sa_family_t); diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index 457211c2a7e..bd43a731387 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtsock.c,v 1.287 2019/06/05 12:53:43 claudio Exp $ */ +/* $OpenBSD: rtsock.c,v 1.288 2019/06/21 17:11:42 mpi Exp $ */ /* $NetBSD: rtsock.c,v 1.18 1996/03/29 00:32:10 cgd Exp $ */ /* @@ -931,7 +931,7 @@ rtm_output(struct rt_msghdr *rtm, struct rtentry **prt, NET_LOCK(); ifp->if_rtrequest(ifp, RTM_INVALIDATE, rt); /* Reset the MTU of the gateway route. */ - rtable_walk(tableid, rt_key(rt)->sa_family, + rtable_walk(tableid, rt_key(rt)->sa_family, NULL, route_cleargateway, rt); NET_UNLOCK(); if_put(ifp); @@ -1911,7 +1911,8 @@ sysctl_rtable(int *name, u_int namelen, void *where, size_t *given, void *new, if (af != 0 && af != i) continue; - error = rtable_walk(tableid, i, sysctl_dumpentry, &w); + error = rtable_walk(tableid, i, NULL, sysctl_dumpentry, + &w); if (error == EAFNOSUPPORT) error = 0; if (error) |