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);