DragonFlyBSD Kernel Audit
DF-0137 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/kern/kern_varsym.c b/sys/kern/kern_varsym.c
--- a/sys/kern/kern_varsym.c
+++ b/sys/kern/kern_varsym.c
@@ -507,7 +507,7 @@
 
     nve = kmalloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO);
     nve->ve_sym = ve->ve_sym;
-    ++nve->ve_sym->vs_refs;	/* can't be reached, no need for atomic add */
+    atomic_add_int(&nve->ve_sym->vs_refs, 1);	/* concurrent varsymdrop races */
     /*
      * We're only called through varsymset_init() so vss is not yet reachable,
      * no need to lock.
@@ -523,10 +523,18 @@
     TAILQ_INIT(&vss->vx_queue);
     lockinit(&vss->vx_lock, "vx", 0, 0);
     if (copy) {
+	/*
+	 * Hold the source varsymset's lock while copying so a concurrent
+	 * varsym_set()/varsymmake() on another LWP of the same proc cannot
+	 * free entries (or race the non-atomic refcount) out from under our
+	 * TAILQ traversal.
+	 */
+	lockmgr(&copy->vx_lock, LK_SHARED);
 	TAILQ_FOREACH(ve, &copy->vx_queue, ve_entry) {
 	    varsymdup(vss, ve);
 	}
 	vss->vx_setsize = copy->vx_setsize;
+	lockmgr(&copy->vx_lock, LK_RELEASE);
     }
 }