Package: vde2
Version: 2.3.2-4.2
Severity: important
Tags: upstream patch

Hello,

We're using the vde2-switch as network switch for several qemu-processes. 
Lately we
noticed a lot of segfaults from the vde2-switch process. We were able to 
trigger the
segfaults more often when we generate a lot of random mac-addresses on the 
network,
combined with a lot of traffic.

After diving into the code, I noticed that the hash_gc() method is called from 
the
SIGALRM signal handler, which could happen at the same time as a find_in_hash() 
or
find_in_hash_update() lookup. The hash_gc() can then invalidate a pointer which 
the
find_in_hash() or find_in_hash_update() call is still using, which causes a 
segfault.

By simply delaying the hash_gc() to the next find_in_hash() or 
find_in_hash_update()
call, it is no longer possible to have invalid pointers. The suggested patch 
does this
by setting the new 'delayed_hash_gc' flag.


ps. Afaics, it is now also safe to remove all qtime_csenter()/qtime_csexit() 
calls
    in hash.c, but I'll leave that to the author of vde2 to verify that.


Regards,

Bas van Sisseren


-- System Information:
Debian Release: jessie/sid
  APT prefers squeeze-lts
  APT policy: (500, 'squeeze-lts'), (500, 'unstable'), (500, 'testing'), (500, 
'stable'), (500, 'oldstable')
Architecture: i386 (x86_64)
Foreign Architectures: amd64

Kernel: Linux 3.16-2-amd64 (SMP w/4 CPU cores)
Locale: LANG=C, LC_CTYPE=C (charmap=ANSI_X3.4-1968)
Shell: /bin/sh linked to /bin/bash

Versions of packages vde2 depends on:
ii  adduser      3.113+nmu3
ii  libc6        2.19-11
ii  libpcap0.8   1.6.2-1
ii  libvde0      2.3.2-4.2
ii  libvdeplug2  2.3.2-4.2

vde2 recommends no packages.

Versions of packages vde2 suggests:
ii  qemu           2.1+dfsg-5
pn  qemu-kvm       <none>
pn  vde2-cryptcab  <none>

-- no debconf information
diff -ru vde2-2.3.2.orig/src/vde_switch/hash.c vde2-2.3.2/src/vde_switch/hash.c
--- vde2-2.3.2.orig/src/vde_switch/hash.c	2011-11-23 17:41:17.000000000 +0100
+++ vde2-2.3.2/src/vde_switch/hash.c	2014-10-07 13:51:35.396596610 +0200
@@ -49,8 +49,11 @@
 	u_int64_t dst;
 };
 
+static int delayed_hash_gc;
 static struct hash_entry **h;
 
+static void hash_gc(void *arg); // forward declaration
+
 static int calc_hash(u_int64_t src)
 {
 	register int x = src * 0x030507090b0d1113LL;
@@ -89,6 +92,7 @@
  * port */
 int find_in_hash(unsigned char *dst,int vlan)
 {
+	if (delayed_hash_gc) hash_gc(NULL);
 	struct hash_entry *e = find_entry(extmac(dst,vlan));
 	if(e == NULL) return -1;
 	return(e->port);
@@ -102,6 +106,7 @@
 	int k = calc_hash(esrc);
 	int oldport;
 	time_t now;
+	if (delayed_hash_gc) hash_gc(NULL);
 	for(e = h[k]; e && e->dst != esrc; e = e->next)
 		;
 	if(e == NULL) {
@@ -267,9 +272,17 @@
 static void hash_gc(void *arg)
 {
 	time_t t = qtime();
+	delayed_hash_gc = 0;
 	for_all_hash(&gc, &t);
 }
 
+/* actual handler which is called every GC_INTERVAL seconds. only set a flag
+   to stay "thread-safe" */
+static void hash_gc_flag(void *arg)
+{
+	delayed_hash_gc++;
+}
+
 #define HASH_INIT(BIT) \
 	({ hash_bits=(BIT);\
 	 hash_mask=HASH_SIZE-1;\
@@ -310,7 +323,7 @@
 {
 	qtimer_del(gc_timerno);
 	gc_interval=p;
-	gc_timerno=qtimer_add(gc_interval,0,hash_gc,NULL);
+	gc_timerno=qtimer_add(gc_interval,0,hash_gc_flag,NULL);
 	return 0;
 }
 
@@ -410,7 +423,7 @@
 
 	gc_interval=GC_INTERVAL;
 	gc_expire=GC_EXPIRE;
-	gc_timerno=qtimer_add(gc_interval,0,hash_gc,NULL);
+	gc_timerno=qtimer_add(gc_interval,0,hash_gc_flag,NULL);
 	ADDCL(cl);
 #ifdef DEBUGOPT
 	ADDDBGCL(dl);

Reply via email to