Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 1894

kernel-2.6.18-238.el5.src.rpm

From: Thomas Graf <tgraf@redhat.com>
Date: Tue, 5 Jan 2010 11:46:52 +0100
Subject: [net] emergency route cache flushing fixes
Message-id: 20100105114630.GB22815@lsx.localdomain
O-Subject: [kernel team] [RHEL5.5 PATCH][EMBARGOED] emergency route cache flushing fixes  (BZ545411)
Bugzilla: 545663
RH-Acked-by: Neil Horman <nhorman@redhat.com>
RH-Acked-by: David Miller <davem@redhat.com>
CVE: CVE-2009-4272

Hello all -

Konstantin Khorenko reported various issues regarding the emergency route
cache flush mechanism:

  1) deadlock while doing an emergency flush, chain is locked twice:

   rt_intern_hash(
     spin_lock_bh(rt_hash_lock_addr(hash));  <-- locked 1st time
     rt_emergency_hash_rebuild
       rt_secret_rebuild_oneshot
         rt_cache_flush
	   rt_run_flush()
	     for (i = rt_hash_mask; i >= 0; i--) {
	       spin_lock_bh(rt_hash_lock_addr(i)); <-- locked again, deadlock

     Upstream is not affected by this as it does not flush the cache
     immediately but instead invalidates it and lets a gc do the flush.

  2) Uninitialized **rp pointer during route lookup. This was fixed upstream
     in commit 73e42897e8e5619eacb787d2ce69be12f47cfc21 but was not back
     ported to RHEL yet.

  3) Although not reported, I also back ported commit
     b6280b47a7a42970d098a3059f4ebe7e55e90d8d to fix a leak and make routes
     useable while caching is off.

Related upstream commits:
  73e42897e8e5619eacb787d2ce69be12f47cfc21
  b6280b47a7a42970d098a3059f4ebe7e55e90d8d

Bugzilla:
  Fixes bugzilla BZ545411

Testing:
  The patch was successfully tested by the customer.

diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 1586943..8251ce8 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1013,8 +1013,35 @@ restart:
 	now = jiffies;
 
 	if (!rt_caching()) {
-		rt_drop(rt);
-		return 0;
+		/*
+		 * If we're not caching, just tell the caller we
+		 * were successful and don't touch the route.  The
+		 * caller hold the sole reference to the cache entry, and
+		 * it will be released when the caller is done with it.
+		 * If we drop it here, the callers have no way to resolve routes
+		 * when we're not caching.  Instead, just point *rp at rt, so
+		 * the caller gets a single use out of the route
+		 * Note that we do rt_free on this new route entry, so that
+		 * once its refcount hits zero, we are still able to reap it
+		 * (Thanks Alexey)
+		 * Note also the rt_free uses call_rcu.  We don't actually
+		 * need rcu protection here, this is just our path to get
+		 * on the route gc list.
+ 		 */
+
+		if (rt->rt_type == RTN_UNICAST || rt->fl.iif == 0) {
+			int err = arp_bind_neighbour(&rt->u.dst);
+			if (err) {
+				if (net_ratelimit())
+					printk(KERN_WARNING
+					    "Neighbour table failure & not caching routes.\n");
+				rt_drop(rt);
+				return err;
+			}
+		}
+
+		rt_free(rt);
+		goto skip_hashing;
 	}
 
 	rthp = &rt_hash_table[hash].chain;
@@ -1098,7 +1125,13 @@ restart:
 				       "limit, route caching disabled\n",
 				rt->u.dst.dev->name, current_rt_cache_rebuild_count);
 			}
+
+			/* We need to unlock here, flushing the cache will lock each chain */
+			spin_unlock_bh(rt_hash_lock_addr(hash));
 			rt_emergency_hash_rebuild();
+
+			/* Start over, someone might have inserted right after the flush */
+			goto restart;
 		}
 	}
 
@@ -1158,6 +1191,7 @@ restart:
 		rt_hash_table[hash].chain = rt;
 
 	spin_unlock_bh(rt_hash_lock_addr(hash));
+skip_hashing:
 	*rp = rt;
 	return 0;
 }