⬢ DragonFlyBSD Kernel Audit
DF-5638 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/smbus/smbconf.c	2026-10-09 01:29:47.869160074 +0000
+++ b/sys/bus/smbus/smbconf.c	2026-10-09 01:45:45.460818539 +0000
@@ -28,6 +28,7 @@
  *
  */
 #include <sys/param.h>
+#include <sys/kernel.h>
 #include <sys/systm.h>
 #include <sys/module.h>
 #include <sys/bus.h>
@@ -86,11 +87,11 @@
 
 	switch (how) {
 	case (SMB_WAIT | SMB_INTR):
-		error = tsleep(sc, PCATCH, "smbreq", 0);
+		error = tsleep(sc, PCATCH, "smbreq", hz);
 		break;
 
 	case (SMB_WAIT | SMB_NOINTR):
-		error = tsleep(sc, 0, "smbreq", 0);
+		error = tsleep(sc, PCATCH, "smbreq", hz);
 		break;
 
 	default:
@@ -128,16 +129,28 @@
 		if (sc->owner && sc->owner != dev) {
 			crit_exit();
 			error = smbus_poll(sc, how);
-			if (error == EWOULDBLOCK)
+			/*
+			 * A woken waiter MUST re-check ownership: the old
+			 * code broke out with success on any wakeup without
+			 * ever setting sc->owner = dev (phantom grant,
+			 * DF-5636).  A poll timeout on a WAIT request is
+			 * the same: re-check.  Only an interruption, or a
+			 * refused SMB_DONTWAIT, unwinds the bridge request.
+			 */
+			if (error == 0)
+				continue;	/* woken: re-check ownership */
+			if (error == EWOULDBLOCK && (how & SMB_WAIT)) {
 				error = 0;
+				continue;	/* poll timeout: re-check */
+			}
+			/* interrupted, or refused SMB_DONTWAIT: unwind */
+			SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, &how);
 		} else {
 			sc->owner = dev;
+			error = 0;	/* claimed: report success */
 			crit_exit();
-		}
-		if (error == 0)
 			break;
-		/* free any allocated resource */
-		SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, &how);
+		}
 	}
 
 	return (error);
@@ -156,23 +169,20 @@
 	int error;
 
 	parent = device_get_parent(bus);
-	/* first, ask the underlying layers if the release is ok */
-	error = SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, NULL);
-
-	if (error)
-		return (error);
 
+	/* validate ownership BEFORE touching the underlying layers */
 	crit_enter();
 	if (sc->owner == dev) {
 		sc->owner = NULL;
-
-		/* wakeup waiting processes */
-		wakeup(sc);
 	} else {
-		error = EACCES;
+		crit_exit();
+		return (EACCES);
 	}
 	crit_exit();
 
+	/* ownership confirmed: release the underlying layers */
+	error = SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, NULL);
+
 	/* wakeup waiting processes */
 	wakeup(sc);