On 06/11/2018 03:16, Max Reitz wrote:
>> diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
>> index c5497bbea8..8fc74ef0bd 100644
>> --- a/hw/scsi/scsi-generic.c
>> +++ b/hw/scsi/scsi-generic.c
>> @@ -16,6 +16,7 @@
>>  #include "qemu-common.h"
>>  #include "qemu/error-report.h"
>>  #include "hw/scsi/scsi.h"
>> +#include "hw/scsi/emulation.h"
>>  #include "sysemu/block-backend.h"
>>  
>>  #ifdef __linux__
>> @@ -209,9 +210,24 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq 
>> *r, SCSIDevice *s)
>>      }
>>  }
>>  
>> -static int scsi_emulate_block_limits(SCSIGenericReq *r)
>> +static int scsi_generic_emulate_block_limits(SCSIGenericReq *r, SCSIDevice 
>> *s)
>>  {
>> -    r->buflen = scsi_disk_emulate_vpd_page(&r->req, r->buf);
>> +    int len, buflen;
> 
> buflen is unused, so this does not compile for me.

Yeah, I forgot to commit it.

>> +    uint8_t buf[64];
>> +
>> +    SCSIBlockLimits bl = {
>> +        .max_io_sectors = blk_get_max_transfer(s->conf.blk) / s->blocksize
>> +    };
>> +
>> +    memset(r->buf, 0, r->buflen);
>> +    stb_p(buf, s->type);
>> +    stb_p(buf + 1, 0xb0);
>> +    len = scsi_emulate_block_limits(buf + 4, &bl);
>> +    assert(len <= sizeof(buf) - 4);
> 
> Let's hope our stack grows downwards, otherwise we'll never get back
> here if there was an overflow.  Maybe it would be better to pass the
> buffer length to scsi_emulate_block_limits() and then move the assertion
> there.

Isn't that a problem always with stacks that grow upwards?  A buffer
overflow on an argument that points to a local variable will always
corrupt the stack frame (on the other hand, a stack that grows downwards
will corrupt the stack frame if the buffer overflow occurs directly in
the function with no call in between).

> With buflen dropped, and you taking full responsibility for any future
> bugs on ABIs with upwards stacks when someone extended
> scsi_emulate_block_limits(), forgetting to adjust the buffer size here
> (:-)):

I think it's quite likely that the bug would be caught first on a
downwards-stack architecture. :)

The complete delta between v1 and v2 will be

diff --git a/hw/scsi/emulation.c b/hw/scsi/emulation.c
index 94c2254bb4..06d62f3c38 100644
--- a/hw/scsi/emulation.c
+++ b/hw/scsi/emulation.c
@@ -3,10 +3,10 @@
 #include "qemu/bswap.h"
 #include "hw/scsi/emulation.h"

-int scsi_emulate_block_limits(uint8_t *outbuf, SCSIBlockLimits *bl)
+int scsi_emulate_block_limits(uint8_t *outbuf, const SCSIBlockLimits *bl)
 {
     /* required VPD size with unmap support */
-    memset(outbuf, 0, 0x3C);
+    memset(outbuf, 0, 0x3c);

     outbuf[0] = bl->wsnz; /* wsnz */

diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index b8fa0a0f7e..7237b4162e 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -182,7 +182,7 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq
*r, SCSIDevice *s)
             /* Also take care of the opt xfer len. */
             stl_be_p(&r->buf[12],
                     MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
-        } else if (page == 0x00 && s->needs_vpd_bl_emulation) {
+        } else if (s->needs_vpd_bl_emulation && page == 0x00) {
             /*
              * Now we're capable of supplying the VPD Block Limits
              * response if the hardware can't. Add it in the INQUIRY
@@ -268,15 +268,14 @@ static void scsi_read_complete(void * opaque, int ret)
      * resulted in sense error but would need emulation.
      * In this case, emulate a valid VPD response.
      */
-    if (ret == 0 &&
+    if (s->needs_vpd_bl_emulation && ret == 0 &&
         (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) &&
-        scsi_parse_sense_buf(r->req.sense, r->io_header.sb_len_wr).key
== ILLEGAL_REQUEST &&
-        s->needs_vpd_bl_emulation) {
-        int is_vpd_bl = r->req.cmd.buf[0] == INQUIRY &&
-                         r->req.cmd.buf[1] & 0x01 &&
-                         r->req.cmd.buf[2] == 0xb0;
-
-        if (is_vpd_bl) {
+        r->req.cmd.buf[0] == INQUIRY &&
+        (r->req.cmd.buf[1] & 0x01) &&
+        r->req.cmd.buf[2] == 0xb0) {
+        SCSISense sense =
+            scsi_parse_sense_buf(r->req.sense, r->io_header.sb_len_wr);
+        if (sense.key == ILLEGAL_REQUEST) {
             len = scsi_generic_emulate_block_limits(r, s);
             /*
              * No need to let scsi_read_complete go on and handle an
diff --git a/include/hw/scsi/emulation.h b/include/hw/scsi/emulation.h
index 42de7c30c2..09fba1ff39 100644
--- a/include/hw/scsi/emulation.h
+++ b/include/hw/scsi/emulation.h
@@ -11,6 +11,6 @@ typedef struct SCSIBlockLimits {
     uint32_t max_io_sectors;
 } SCSIBlockLimits;

-int scsi_emulate_block_limits(uint8_t *outbuf, SCSIBlockLimits *bl);
+int scsi_emulate_block_limits(uint8_t *outbuf, const SCSIBlockLimits *bl);

 #endif

with most of the changes being just cosmetic to avoid the long line
without reindenting everything.

Paolo

> Reviewed-by: Max Reitz <mre...@redhat.com>
> 
>> +    stw_be_p(buf + 2, len);
>> +
>> +    memcpy(r->buf, buf, MIN(r->buflen, len + 4));
>> +
>>      r->io_header.sb_len_wr = 0;
>>  
>>      /*
> 


Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to