⬢ DragonFlyBSD Kernel Audit
DF-2310 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/iicbus/iic.c
+++ b/sys/bus/iicbus/iic.c
@@ -291,6 +291,7 @@
 	struct iic_rdwr_data *d = (struct iic_rdwr_data *)data;
 	struct iic_msg *m;
 	int error, count, i;
+	int nalloc;			/* DF-2310: buffers actually alloc'd */
 	char *buf = NULL;
 	void **usrbufs = NULL;
 
@@ -354,19 +355,33 @@
 		if (error)
 			break;
 		/* Alloc kernel buffers for userland data, copyin write data */
+		nalloc = 0;
 		for (i = 0; i < d->nmsgs; i++) {
 			m = &((struct iic_msg *)buf)[i];
 			usrbufs[i] = m->buf;
-			m->buf = kmalloc(m->len, M_TEMP, M_WAITOK);
-			if (!(m->flags & IIC_M_RD))
-				copyin(usrbufs[i], m->buf, m->len);
+			m->buf = kmalloc(m->len, M_TEMP, M_WAITOK | M_ZERO);
+			nalloc++;
+			/*
+			 * DF-2310: an unchecked copyin here silently
+			 * transmits uninitialized kernel heap to the I2C
+			 * peripheral; propagate the fault and skip the
+			 * transfer.
+			 */
+			if (!(m->flags & IIC_M_RD)) {
+				error = copyin(usrbufs[i], m->buf, m->len);
+				if (error != 0)
+					break;
+			}
 		}
-		error = iicbus_transfer(parent, (struct iic_msg *)buf, d->nmsgs);
+		if (error == 0)
+			error = iicbus_transfer(parent,
+			    (struct iic_msg *)buf, d->nmsgs);
 		/* Copyout all read segments, free up kernel buffers */
-		for (i = 0; i < d->nmsgs; i++) {
+		for (i = 0; i < nalloc; i++) {
 			m = &((struct iic_msg *)buf)[i];
-			if (m->flags & IIC_M_RD)
-				copyout(m->buf, usrbufs[i], m->len);
+			if (error == 0 && (m->flags & IIC_M_RD) &&
+			    copyout(m->buf, usrbufs[i], m->len) != 0)
+				error = EFAULT;	/* DF-2310: report it */
 			kfree(m->buf, M_TEMP);
 		}
 		kfree(usrbufs, M_TEMP);