diff options
author | ru <ru@FreeBSD.org> | 2006-11-25 20:38:56 +0000 |
---|---|---|
committer | ru <ru@FreeBSD.org> | 2006-11-25 20:38:56 +0000 |
commit | 667552ba9608e4549b8100cd0324113c51c00cd2 (patch) | |
tree | c134bec3e2d366e8153187510cff3c5dc5dbd804 /sys | |
parent | a1e98617efaeebaf58f8744cc6c55b243f437e06 (diff) | |
download | FreeBSD-src-667552ba9608e4549b8100cd0324113c51c00cd2.zip FreeBSD-src-667552ba9608e4549b8100cd0324113c51c00cd2.tar.gz |
- In nd6_rtrequest(), when caching an rtentry, don't forget
to add a reference to it; otherwise, we could later access
a freed memory. This is believed to fix panics some users
were observing when running route6d(8), and is similar to
the fix in sys/netinet/if_ether.c,v 1.139 by glebius@.
PR: kern/93910, kern/105437
Testing by: Wojciech Puchar (still ongoing)
- Add rtentry locking to nd6_output() similar to rt_check().
MFC after: 4 days
Diffstat (limited to 'sys')
-rw-r--r-- | sys/netinet6/nd6.c | 38 |
1 files changed, 29 insertions, 9 deletions
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 16a5294..39782e8 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1310,6 +1310,7 @@ nd6_rtrequest(req, rt, info) nd6_inuse++; nd6_allocated++; bzero(ln, sizeof(*ln)); + RT_ADDREF(rt); ln->ln_rt = rt; callout_init(&ln->ln_timer_ch, 0); @@ -1420,6 +1421,7 @@ nd6_rtrequest(req, rt, info) ln->ln_prev->ln_next = ln->ln_next; ln->ln_prev = NULL; nd6_llinfo_settimer(ln, -1); + RT_REMREF(rt); rt->rt_llinfo = 0; rt->rt_flags &= ~RTF_LLINFO; clear_llinfo_pqueue(ln); @@ -1955,13 +1957,16 @@ nd6_output(ifp, origifp, m0, dst, rt0) /* * next hop determination. This routine is derived from ether_output. */ + /* NB: the locking here is tortuous... */ + if (rt != NULL) + RT_LOCK(rt); again: - if (rt) { + if (rt != NULL) { if ((rt->rt_flags & RTF_UP) == 0) { + RT_UNLOCK(rt); rt0 = rt = rtalloc1((struct sockaddr *)dst, 1, 0UL); if (rt != NULL) { RT_REMREF(rt); - RT_UNLOCK(rt); if (rt->rt_ifp != ifp) /* * XXX maybe we should update ifp too, @@ -1986,6 +1991,7 @@ again: */ if (!nd6_is_addr_neighbor(gw6, ifp) || in6ifa_ifpwithaddr(ifp, &gw6->sin6_addr)) { + RT_UNLOCK(rt); /* * We allow this kind of tricky route only * when the outgoing interface is p2p. @@ -1997,18 +2003,32 @@ again: goto sendpkt; } - if (rt->rt_gwroute == 0) + if (rt->rt_gwroute == NULL) goto lookup; - if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { - RT_LOCK(rt); - rtfree(rt); rt = rt0; + rt = rt->rt_gwroute; + RT_LOCK(rt); /* NB: gwroute */ + if ((rt->rt_flags & RTF_UP) == 0) { + rtfree(rt); /* unlock gwroute */ + rt = rt0; lookup: - rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 0UL); - if ((rt = rt->rt_gwroute) == 0) + RT_UNLOCK(rt0); + rt = rtalloc1(rt->rt_gateway, 1, 0UL); + if (rt == rt0) { + rt0->rt_gwroute = NULL; + RT_REMREF(rt0); + RT_UNLOCK(rt0); senderr(EHOSTUNREACH); - RT_UNLOCK(rt); + } + RT_LOCK(rt0); + rt0->rt_gwroute = rt; + if (rt == NULL) { + RT_UNLOCK(rt0); + senderr(EHOSTUNREACH); + } } + RT_UNLOCK(rt0); } + RT_UNLOCK(rt); } /* |