Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 17 Jan 2009 16:13:03 GMT
From:      Dmitrij Tejblum <tejblum@yandex-team.ru>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   kern/130652: Possible deadlock in rt_check() (sys/net/route.c)
Message-ID:  <200901171613.n0HGD3Qj009412@www.freebsd.org>
Resent-Message-ID: <200901171620.n0HGK3Ta079790@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         130652
>Category:       kern
>Synopsis:       Possible deadlock in rt_check() (sys/net/route.c)
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat Jan 17 16:20:03 UTC 2009
>Closed-Date:
>Last-Modified:
>Originator:     Dmitrij Tejblum
>Release:        7.1-STABLE
>Organization:
OOO Yandex
>Environment:
FreeBSD 7.1-STABLE; net/route.c 1.120.2.7
>Description:
Some excerpt from rt_check(): 
rt_check()
{
/*1*/   RT_LOCK(rt0);
retry:
        ...
                if ((rt = rt0->rt_gwroute) != NULL) {
/*2*/                   RT_LOCK(rt);            /* NB: gwroute */
                        ....
                }
                 
                if (rt == NULL) {  /* NOT AN ELSE CLAUSE */
/*3*/                   RT_TEMP_UNLOCK(rt0); /* MUST return to undo this */
/*4*/                   rt = rtalloc1_fib(rt0->rt_gateway, 1, 0UL, fibnum);
                        ....
/*5*/                   RT_RELOCK(rt0);
                        ....
                                rt0->rt_gwroute = rt;
                }
                RT_LOCK_ASSERT(rt);
                RT_UNLOCK(rt0);
                ....
}

The function deals with route rt0 and rt. Usually, it locks rt0 in point /*1*/, then locks rt = rt0->rt_gwroute in point /*2*/, then unlock rt0 and done. But sometimes, in lock rt inside rtalloc1_fib() in point /*4*/. Then, in point /*5*/, it locks rt0, which was unlocked in point /*3*/. The order of locking of rt0 and rt is reversed, so a deadlock is possible.

(Also, if after RT_RELOCK(rt0) we found that rt0 is unusable, we should not forget to free rt before retry.)

>How-To-Repeat:

>Fix:


Patch attached with submission follows:

--- net/route.c	2008-12-05 20:40:46.000000000 +0300
+++ net/route.c	2009-01-17 18:59:12.000000000 +0300
@@ -1634,24 +1634,31 @@ retry:
 			 * Relock it and lose the added reference.
 			 * All sorts of things could have happenned while we
 			 * had no lock on it, so check for them.
+			 * rt need to be unlocked to avoid possible deadlock.
 			 */
+			RT_UNLOCK(rt);
 			RT_RELOCK(rt0);
-			if (rt0 == NULL || ((rt0->rt_flags & RTF_UP) == 0))
+			if (rt0 == NULL || ((rt0->rt_flags & RTF_UP) == 0)) {
 				/* Ru-roh.. what we had is no longer any good */
+				RTFREE(rt);
 				goto retry;
+			}
 			/* 
 			 * While we were away, someone replaced the gateway.
 			 * Since a reference count is involved we can't just
 			 * overwrite it.
 			 */
 			if (rt0->rt_gwroute) {
-				if (rt0->rt_gwroute != rt) {
-					RTFREE_LOCKED(rt);
-					goto retry;
-				}
+				if (rt0->rt_gwroute != rt)
+					RTFREE(rt);
 			} else {
 				rt0->rt_gwroute = rt;
 			}
+			/* 
+			 * Since rt was not locked, we need recheck that
+			 * it still may be used (e.g. up)
+			 */
+			goto retry;
 		}
 		RT_LOCK_ASSERT(rt);
 		RT_UNLOCK(rt0);


>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200901171613.n0HGD3Qj009412>