DragonFlyBSD Kernel Audit
DF-2468 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/disk/iscsi/initiator/iscsi_subr.c b/sys/dev/disk/iscsi/initiator/iscsi_subr.c
--- a/sys/dev/disk/iscsi/initiator/iscsi_subr.c
+++ b/sys/dev/disk/iscsi/initiator/iscsi_subr.c
@@ -151,13 +151,26 @@
      bp = mtod(pq->mp, caddr_t);
      if((sense_len = scsi_2btoul(bp)) == 0)
 	  return 0;
+     /*
+      | The 2-byte sense_len is read directly off the (target-controlled) Data
+      | Segment and is NOT bounded by the actual Data Segment length the
+      | target sent (pp->ds_len).  Without this clamp, a target that claims a
+      | large sense_len but sends a short Data Segment makes us kmalloc() a
+      | big scratch buffer that i_mbufcopy() only partially fills, leaving the
+      | rest as stale kernel heap which is then bcopy()'d into the CCB sense
+      | buffer (info leak), and can also read 2 bytes past the allocation.
+      */
+     if(pp->ds_len < 2 || sense_len > pp->ds_len - 2)
+	  sense_len = (pp->ds_len >= 2) ? pp->ds_len - 2 : 0;
+     if(sense_len == 0)
+	  return 0;
      debug(4, "sense_len=%d", sense_len);
      /*
       | according to the specs, the sense data cannot
       | be larger than 252 ...
       */
      if(sense_len > m->m_len) {
-	  bp = kmalloc(sense_len, M_ISCSI, M_WAITOK);
+	  bp = kmalloc(sense_len, M_ISCSI, M_WAITOK | M_ZERO);
 	  debug(3, "calling i_mbufcopy(len=%d)", sense_len);
 	  i_mbufcopy(pq->mp, bp, sense_len);
 	  mustfree++;