NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/43349: if_detach causes kernel crash



>Number:         43349
>Category:       kern
>Synopsis:       if_detach causes kernel crash
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon May 24 19:15:00 +0000 2010
>Originator:     Onno van der Linden
>Release:        5.99.29 i386
>Organization:
>Environment:
NetBSD sheep 5.99.29 NetBSD 5.99.29 (SHEEP) #3: Mon May 17 22:50:14 MEST 2010  
root@sheep:/usr/src/sys/arch/i386/compile/SHEEP i386

>Description:
Same crash as described in 
http://mail-index.netbsd.org/tech-kern/2009/10/28/msg006374.html.
Likely to be caused (in my case at least) by a cloned route: removing those by 
hand followed by
a reboot/halt/shutdown avoids the crash in rn_walktree.  The optimizer inlines 
the call to rn_walknext
which is where the the crash happens because  rn=rn->rn_l results in rn getting 
a NULL value
which gets referenced  at the next iteration of the for loop.





>How-To-Repeat:
If your route table has a cloned route it'll probably crash every time you use 
shutdown/reboot/halt.
>Fix:
Based on http://openbsd.monkey.org/tech/200603/msg00007.html
I made a fix myself and a little later I came across 
http://mail-index.netbsd.org/tech-kern/2009/10/28/msg006380.html which 
according to
http://mail-index.netbsd.org/tech-kern/2009/11/04/msg006433.html works fine too
and is a lot shorter than the diff I made.

*** /sys/net/if.c       Thu Jan 28 15:12:11 2010
--- /sys/net/if.c.fixed Mon May 17 22:48:34 2010
***************
*** 792,799 ****
        if_free_sadl(ifp);
  
        /* Walk the routing table looking for stragglers. */
!       for (i = 0; i <= AF_MAX; i++)
!               (void)rt_walktree(i, if_rt_walktree, ifp);
  
        DOMAIN_FOREACH(dp) {
                if (dp->dom_ifdetach != NULL && ifp->if_afdata[dp->dom_family])
--- 792,801 ----
        if_free_sadl(ifp);
  
        /* Walk the routing table looking for stragglers. */
!       for (i = 0; i <= AF_MAX; i++) {
!               while (rt_walktree(i, if_rt_walktree, ifp) == EAGAIN)
!                       ;
!       }
  
        DOMAIN_FOREACH(dp) {
                if (dp->dom_ifdetach != NULL && ifp->if_afdata[dp->dom_family])
***************
*** 884,899 ****
  /*
   * Callback for a radix tree walk to delete all references to an
   * ifnet.
   */
  static int
  if_rt_walktree(struct rtentry *rt, void *v)
  {
        struct ifnet *ifp = (struct ifnet *)v;
!       int error;
  
        if (rt->rt_ifp != ifp)
                return 0;
  
        /* Delete the entry. */
        ++rt->rt_refcnt;
        error = rtrequest(RTM_DELETE, rt_getkey(rt), rt->rt_gateway,
--- 886,906 ----
  /*
   * Callback for a radix tree walk to delete all references to an
   * ifnet.
+  * Note that deleting a RTF_CLONING route can trigger the
+  * deletion of more entries, so we need to cancel the walk
+  * and return EAGAIN.  The caller should restart the walk
+  * as long as EAGAIN is returned.
   */
  static int
  if_rt_walktree(struct rtentry *rt, void *v)
  {
        struct ifnet *ifp = (struct ifnet *)v;
!       int error, cloning;
  
        if (rt->rt_ifp != ifp)
                return 0;
  
+       cloning = (rt->rt_flags & RTF_CLONING);
        /* Delete the entry. */
        ++rt->rt_refcnt;
        error = rtrequest(RTM_DELETE, rt_getkey(rt), rt->rt_gateway,
***************
*** 904,909 ****
--- 911,918 ----
        if (error != 0)
                printf("%s: warning: unable to delete rtentry @ %p, "
                    "error = %d\n", ifp->if_xname, rt, error);
+       else if (cloning)
+               return EAGAIN;
        return 0;
  }



Home | Main Index | Thread Index | Old Index