⬢ DragonFlyBSD Kernel Audit
DF-2814 / fix.diff
← back to finding ↓ download raw
--- sys/kern/kern_objcache.c	2026-06-29 12:51:19.000000000 +0000
+++ /tmp/opencode/v26/fixtree/sys/kern/kern_objcache.c	2026-10-24 16:33:07.789715562 +0000
@@ -149,6 +149,8 @@
 
 	struct objcache_desc	*desc;
 
+	int			oc_dying;	/* DF-2814 */
+
 	/* NUMA-cluster level caches */
 	struct magazinedepot	depot[MAXCLUSTERS];
 
@@ -432,6 +434,12 @@
 	void *obj;
 	struct magazinedepot *depot;
 
+	/*
+	 * DF-2814: refuse allocations from a cache being destroyed.
+	 */
+	if (oc->oc_dying)
+		return (NULL);
+
 	KKASSERT((ocflags & M_ZERO) == 0);
 	crit_enter();
 	++cpucache->gets_cumulative;
@@ -623,6 +631,17 @@
 	struct magazine *loadedmag;
 	struct magazinedepot *depot;
 
+	/*
+	 * DF-2814: a cache being destroyed may still receive puts from
+	 * in-flight users; dispose directly instead of touching freed
+	 * magazines.
+	 */
+	if (oc->oc_dying) {
+		oc->dtor(obj, oc->privdata);
+		oc->free(obj, oc->allocator_args);
+		return;
+	}
+
 	crit_enter();
 	++cpucache->puts_cumulative;
 
@@ -902,6 +921,15 @@
 }
 
 /*
+ * DF-2814: no-op IPI used as a cross-cpu barrier during destroy.
+ */
+static void
+objcache_noop_ipi(void *arg __unused, int arg2 __unused,
+		  struct intrframe *frame __unused)
+{
+}
+
+/*
  * Destroy an object cache.  Must have no existing references.
  */
 void
@@ -917,6 +945,27 @@
 	LIST_REMOVE(desc, next);
 	spin_unlock(&objcachelist_spin);
 
+	/*
+	 * DF-2814: mark the cache dying, then barrier against every cpu
+	 * so in-flight get/put operations have completed before we rip
+	 * the per-cpu magazines out from under them.  The per-cpu
+	 * magazine contract is crit_enter protection on the OWNING cpu;
+	 * an IPI only executes outside a critical section, so after the
+	 * synchronize no cpu can hold a stale magazine reference.
+	 */
+	oc->oc_dying = 1;
+	cpu_sfence();
+	{
+		int bcpu;
+		for (bcpu = 0; bcpu < ncpus; ++bcpu) {
+			if (globaldata_find(bcpu) == mycpu)
+				continue;
+			lwkt_send_ipiq3(globaldata_find(bcpu),
+					objcache_noop_ipi, NULL, 0);
+		}
+		lwkt_synchronize_ipiqs("objcache_destroy");
+	}
+
 	SLIST_INIT(&tmplist);
 	for (clusterid = 0; clusterid < MAXCLUSTERS; clusterid++) {
 		depot = &oc->depot[clusterid];