Patch against 2.0.3x for race condition in route.c

David Luyer (luyer@ucs.uwa.edu.au)
Tue, 07 Jul 1998 15:24:31 +0800


Alan & Linux-kernel,

This patch adds a little bit of debugging (only if RT_CACHE_DEBUG is
set) and fixes a race condition which would cause an error
'kfree of non-kmalloced memory: ...'
under certain situations (one of which was easily reproducible for me,
as it was something I wanted to actually do!).

The race condition comes from (it appears) a call through ip_rt_unlock()
causing ip_rt_run_bh() causing rt_kick_free_queue() which gets interrupted
by a timer just after the sti() causing ip_rt_check_expire() to be called
(from arp_check_expire()), where it finds ip_rt_lock == 0, grabs the lock,
and causes a second task to enter into the rt_kick_free_queue(). As if that
wasn't bad enough, on the exit from the second task's rt_kick_free_queue() if
the first task hasn't been brought back yet, it calls ip_rt_unlock() which
may cause a third entry to rt_kick_free_queue() and trigger a chain of events
leading to over 12,000 entries to rt_kick_free_queue() inside 2 seconds.

If any other task has done a kmalloc() between the entries to
rt_kick_free_queue() there is a chance for a random memory scribble of
a NULL over 4 bytes of the new allocation if a different 4 bytes of it is
zero.

David.

(rt_kick_free_queue runs with interrupts disabled in the sections this patch
covers so the non-atomic increments and decrements below are safe; also
the lack of a check for actually holding the route cache lock follows how
things are already done in ip_rt_run_bh())

--- linux/net/ipv4/route.c.ORIG Tue Jul 7 15:15:37 1998
+++ linux/net/ipv4/route.c Tue Jul 7 15:17:45 1998
@@ -925,6 +925,15 @@
static __inline__ void rt_kick_free_queue(void)
{
struct rtable *rt, **rtp;
+#if RT_CACHE_DEBUG >= 2
+ static int in = 0;
+
+ if(in) {
+ printk("Attempted multiple entry: rt_kick_free_queue\n");
+ return;
+ }
+ in++;
+#endif

ip_rt_bh_mask &= ~RT_BH_FREE;

@@ -952,6 +961,9 @@
}
rtp = &rt->rt_next;
}
+#if RT_CACHE_DEBUG >= 2
+ in--;
+#endif
}

void ip_rt_run_bh()
@@ -974,8 +986,11 @@
ip_rt_fast_unlock();
}

- if (ip_rt_bh_mask & RT_BH_FREE)
+ if (ip_rt_bh_mask & RT_BH_FREE) {
+ ip_rt_fast_lock();
rt_kick_free_queue();
+ ip_rt_fast_unlock();
+ }
}
restore_flags(flags);
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu