⬢ DragonFlyBSD Kernel Audit
DF-5522 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/iicbus/iicsmb.c
+++ b/sys/bus/iicbus/iicsmb.c
@@ -464,6 +464,8 @@
 {
 	device_t parent = device_get_parent(dev);
 	int error, sent;
+	u_char cnt = count;
+
 
 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
 		goto error;
@@ -471,6 +473,9 @@
 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
 		goto error;
 
+	/* SMBus Block Write: the byte-count precedes the data on the wire */
+	if ((error = iicbus_write(parent, (const char *)&cnt, 1, &sent, IICBUS_TIMEOUT)))
+		goto error;
 	if ((error = iicbus_write(parent, buf, (int)count, &sent, IICBUS_TIMEOUT)))
 		goto error;
 
@@ -486,6 +491,10 @@
 {
 	device_t parent = device_get_parent(dev);
 	int error, sent, read;
+	u_char cnt, devcnt;
+
+	if (*count < 1 || *count > 32)	/* SMBus spec 2.0 block limit */
+		return (SMB_EINVAL);
 
 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
 		return (error);
@@ -496,10 +505,22 @@
 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
 		goto error;
 
-	if ((error = iicbus_read(parent, buf, (int)*count, &read,
-						IIC_LAST_READ, IICBUS_TIMEOUT)))
-		goto error;
-	*count = read;
+	/*
+	 * SMBus Block Read: the first byte the device sends after the
+	 * repeated start is the block count; consume and honour it instead
+	 * of blindly transferring the caller's count of raw bytes.
+	 */
+	if ((error = iicbus_read(parent, (char *)&devcnt, 1, &read,
+					IIC_LAST_READ, IICBUS_TIMEOUT)))
+		goto error;
+	if (devcnt < 1 || devcnt > 32)
+		devcnt = 32;
+	cnt = (devcnt > *count) ? *count : devcnt;
+
+	if ((error = iicbus_read(parent, buf, (int)cnt, &read,
+					IIC_LAST_READ, IICBUS_TIMEOUT)))
+		goto error;
+	*count = cnt;
 
 error:
 	iicbus_stop(parent);