DragonFlyBSD Kernel Audit
DF-2832 / fix.diff
← back to finding ↓ download raw
--- a/sys/kern/vfs_sync.c
+++ b/sys/kern/vfs_sync.c
@@ -198,6 +198,8 @@
 	int slot;
 
 	ctx = vp->v_mount->mnt_syncer_ctx;
+	if (ctx == NULL)
+		return;
 	lwkt_gettoken(&ctx->sc_token);
 
 	if (vp->v_flag & VONWORKLST) {
@@ -234,6 +236,8 @@
 	struct syncer_ctx *ctx;
 
 	ctx = vp->v_mount->mnt_syncer_ctx;
+	if (ctx == NULL)
+		return;
 	lwkt_gettoken(&ctx->sc_token);
 
 	if ((vp->v_flag & (VISDIRTY | VONWORKLST | VOBJDIRTY)) == VONWORKLST &&
@@ -281,6 +285,8 @@
 	if ((vp->v_flag & VISDIRTY) == 0) {
 		ctx = vp->v_mount->mnt_syncer_ctx;
 		vsetflags(vp, VISDIRTY);
+		if (ctx == NULL)
+			return;
 		lwkt_gettoken(&ctx->sc_token);
 		if ((vp->v_flag & VONWORKLST) == 0)
 			vn_syncer_add(vp, syncdelay);
@@ -296,6 +302,8 @@
 	if ((vp->v_flag & VOBJDIRTY) == 0) {
 		ctx = vp->v_mount->mnt_syncer_ctx;
 		vsetflags(vp, VOBJDIRTY);
+		if (ctx == NULL)
+			return;
 		lwkt_gettoken(&ctx->sc_token);
 		if ((vp->v_flag & VONWORKLST) == 0)
 			vn_syncer_add(vp, syncdelay);
@@ -350,11 +358,20 @@
 		lwkt_gettoken(&ctx->sc_token);
 	}
 
-	mp->mnt_syncer_ctx = NULL;
 	lwkt_reltoken(&ctx->sc_token);
 
+	/*
+	 * Interlock against the lock-free trigger_syncer*() / speedup_syncer()
+	 * users, which load mp->mnt_syncer_ctx and then operate on the ctx
+	 * without any other synchronization. Publish the NULL and free the
+	 * ctx while holding mnt_token exclusively so any user that already
+	 * loaded the pointer completes before the memory is destroyed.
+	 */
+	lwkt_gettoken(&mp->mnt_token);
+	mp->mnt_syncer_ctx = NULL;
 	hashdestroy(ctx->syncer_workitem_pending, M_DEVBUF, ctx->syncer_mask);
 	kfree(ctx, M_TEMP);
+	lwkt_reltoken(&mp->mnt_token);
 }
 
 struct  thread *updatethread;
@@ -567,8 +584,12 @@
 	 */
 	atomic_add_int(&rushjob, 1);
 	++stat_rush_requests;
-	if (mp && mp->mnt_syncer_ctx)
-		wakeup(mp->mnt_syncer_ctx);
+	if (mp) {
+		lwkt_gettoken_shared(&mp->mnt_token);
+		if (mp->mnt_syncer_ctx)
+			wakeup(mp->mnt_syncer_ctx);
+		lwkt_reltoken(&mp->mnt_token);
+	}
 }
 
 /*
@@ -581,10 +602,14 @@
 {
 	struct syncer_ctx *ctx;
 
-	if (mp && (ctx = mp->mnt_syncer_ctx) != NULL) {
+	if (mp == NULL)
+		return;
+	lwkt_gettoken_shared(&mp->mnt_token);
+	if ((ctx = mp->mnt_syncer_ctx) != NULL) {
 		if (atomic_fetchadd_int(&ctx->syncer_trigger, 2) <= 1)
 			wakeup(ctx);
 	}
+	lwkt_reltoken(&mp->mnt_token);
 }
 
 void
@@ -592,9 +617,13 @@
 {
 	struct syncer_ctx *ctx;
 
-	if (mp && (ctx = mp->mnt_syncer_ctx) != NULL) {
+	if (mp == NULL)
+		return;
+	lwkt_gettoken_shared(&mp->mnt_token);
+	if ((ctx = mp->mnt_syncer_ctx) != NULL) {
 		atomic_add_int(&ctx->syncer_trigger, -2);
 	}
+	lwkt_reltoken(&mp->mnt_token);
 }
 
 /*
@@ -605,12 +634,16 @@
 {
 	struct syncer_ctx *ctx;
 
-	if (mp && (ctx = mp->mnt_syncer_ctx) != NULL) {
+	if (mp == NULL)
+		return;
+	lwkt_gettoken_shared(&mp->mnt_token);
+	if ((ctx = mp->mnt_syncer_ctx) != NULL) {
 		if ((ctx->syncer_trigger & 1) == 0) {
 			atomic_set_int(&ctx->syncer_trigger, 1);
 			wakeup(ctx);
 		}
 	}
+	lwkt_reltoken(&mp->mnt_token);
 }
 
 /*