--- 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);