diff options
author | ru <ru@FreeBSD.org> | 2002-12-23 13:12:41 +0000 |
---|---|---|
committer | ru <ru@FreeBSD.org> | 2002-12-23 13:12:41 +0000 |
commit | 6b01296394ebb3095ef355f1a37fdcf6b6e3f55e (patch) | |
tree | 352cc529457a0f69f8f554686b7b6bef83659d7d /sys | |
parent | bdd4b4e79835bd9f82877f38431557c94f6ad430 (diff) | |
download | FreeBSD-src-6b01296394ebb3095ef355f1a37fdcf6b6e3f55e.zip FreeBSD-src-6b01296394ebb3095ef355f1a37fdcf6b6e3f55e.tar.gz |
rn_walktree*() compute the next leaf before applying a function
to current leaves because function may vanish the current node.
If parent RTA_GENMASK route has a clone (a "cloning clone"), an
rn_walktree_from() starting from parent will cause another walk
starting from clone. If a function is either rt_fixdelete() or
rt_fixchange(), this recursive walk may vanish the leaf that is
remembered by an outer walk (the "next leaf" above), panicing a
system when it resumes with an outer walk.
The following script paniced my single-user mode booted system:
: sysctl net.inet.ip.forwarding=1
: ipfw add 1 allow ip from any to any
: ifconfig lo0 127.1
: route add -net 10 -genmask 255.255.255.0 127.1
: telnet 10.1 # rt_fixchange() panic
: telnet 10.2
: telnet 10.1
: route delete -net 10 # rt_fixdelete() panic
For the time being, avoid these races by disallowing recursive
walks in rt_fixchange() and rt_fixdelete().
Also, make a slight optimization in the rtrequest(RTM_RESOLVE)
case: there is no reason to call rt_fixchange() in this case.
PR: kern/37606
MFC after: 5 days
Diffstat (limited to 'sys')
-rw-r--r-- | sys/net/route.c | 11 |
1 files changed, 7 insertions, 4 deletions
diff --git a/sys/net/route.c b/sys/net/route.c index bb07949..1727d5b 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -741,7 +741,8 @@ rtrequest1(req, info, ret_nrt) * it doesn't fire when we call it there because the node * hasn't been added to the tree yet. */ - if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) { + if (req == RTM_ADD && + !(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) { struct rtfc_arg arg; arg.rnh = rnh; arg.rt0 = rt; @@ -787,7 +788,8 @@ rt_fixdelete(rn, vp) struct rtentry *rt = (struct rtentry *)rn; struct rtentry *rt0 = vp; - if (rt->rt_parent == rt0 && !(rt->rt_flags & RTF_PINNED)) { + if (rt->rt_parent == rt0 && + !(rt->rt_flags & (RTF_PINNED | RTF_CLONING | RTF_PRCLONING))) { return rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt), rt->rt_flags, (struct rtentry **)0); @@ -829,9 +831,10 @@ rt_fixchange(rn, vp) printf("rt_fixchange: rt %p, rt0 %p\n", rt, rt0); #endif - if (!rt->rt_parent || (rt->rt_flags & RTF_PINNED)) { + if (!rt->rt_parent || + (rt->rt_flags & (RTF_PINNED | RTF_CLONING | RTF_PRCLONING))) { #ifdef DEBUG - if(rtfcdebug) printf("no parent or pinned\n"); + if(rtfcdebug) printf("no parent, pinned or cloning\n"); #endif return 0; } |