zfcp_fc_incoming_rscn() takes the number of RSCN pages from rscn_plen in the received ELS and walks that many four byte pages out of the status read buffer payload. The fabric chooses rscn_plen, it is a 16 bit field, and nothing compares it against the payload. A value of 65535 gives 16383 entries, so the loop reads up to 65532 bytes out of a payload of FSF_STATUS_READ_PAYLOAD_SIZE, which is 4032. The status read buffer is a single page and its payload ends at the page boundary, so the walk continues into whatever follows that page. Every iteration also hands the four bytes it just read to zfcp_fc_enqueue_event(), and that event reaches user space through fc_host_post_event(), so the bytes past the payload are reported and not merely read. libfc bounds the same field. fc_disc_recv_rscn_req() refuses an RSCN whose rscn_plen is not covered by the received frame. zfcp does not share that code, so add the equivalent test here. Compare against the payload size rather than the firmware reported srb->length, because a bogus srb->length need not reach the payload at all. A conforming RSCN is unaffected. An FC data field holds at most 2112 bytes, so at most 528 pages, well under the 1008 the payload can hold. This is an out of bounds read and an information leak to user space. It is not a write and it corrupts no state. It assumes the adapter passes rscn_plen through unchanged, the same assumption the host bus adapter mediated paths in other drivers already make, and one the driver cannot check for itself. Not tested on hardware. The bound follows from the field widths and the payload size. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Yehyeong Lee --- drivers/s390/scsi/zfcp_fc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index 05d0812978798..24efd29b66bfc 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -270,6 +270,18 @@ static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req) no_entries = be16_to_cpu(head->rscn_plen) / sizeof(struct fc_els_rscn_page); + /* + * The fabric chooses rscn_plen, so refuse a value that would walk + * the page list past the end of the status read payload. + */ + if (no_entries > FSF_STATUS_READ_PAYLOAD_SIZE / + sizeof(struct fc_els_rscn_page)) { + dev_warn(&adapter->ccw_device->dev, + "An RSCN with a payload length of %u was dropped\n", + be16_to_cpu(head->rscn_plen)); + return; + } + if (no_entries > 1) { /* handle failed ports */ unsigned long flags; base-commit: f47d223627f80cb7a1eb14c585fe6cb35de5e984 -- 2.43.0