DragonFlyBSD Kernel Audit
DF-1095 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/bus/iicbus/iicsmb.c b/sys/bus/iicbus/iicsmb.c
index 0000000..1111111 100644
--- a/sys/bus/iicbus/iicsmb.c
+++ b/sys/bus/iicbus/iicsmb.c
@@ -465,8 +465,14 @@
 	device_t parent = device_get_parent(dev);
 	int error, sent;
 
+	/*
+	 * On start failure the bus was never placed in the STARTED state,
+	 * so iicbus_stop() must NOT be issued -- it would be a no-op at
+	 * best and a protocol violation at worst.  Return directly, mirroring
+	 * iicsmb_bread() below.  DF-1095.
+	 */
 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
-		goto error;
+		return (error);
 
 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
 		goto error;
@@ -474,10 +480,18 @@
 	if ((error = iicbus_write(parent, buf, (int)count, &sent, IICBUS_TIMEOUT)))
 		goto error;
 
-	if ((error = iicbus_stop(parent)))
-		goto error;
-
 error:
+	/*
+	 * If start succeeded but a subsequent write failed (NACK/bus
+	 * error/timeout), the bus is still STARTED and MUST be released.
+	 * The previous code returned here without calling iicbus_stop(),
+	 * leaving sc->started set (iiconf.c:178) so every subsequent
+	 * iicbus_start() returned EINVAL and the SMBus bridge was
+	 * permanently wedged.  Always issue STOP -- on the success path
+	 * the comment that originally guided the explicit iicbus_stop()
+	 * above is preserved here.  DF-1095.
+	 */
+	iicbus_stop(parent);
 	return (error);
 }