| From 3f4a2e18f357a82186ce02018e42a24b5db4154d Mon Sep 17 00:00:00 2001 |
| From: Asias He <asias@redhat.com> |
| Date: Fri, 11 Oct 2013 10:36:39 +0800 |
| Subject: [PATCH] scsi: Allocate SCSITargetReq r->buf dynamically [CVE-2013-4344] |
| |
| BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1007334 |
| Brew: https://brewweb.devel.redhat.com/taskinfo?taskID=6402920 |
| RH-Author: Asias He <asias@redhat.com> |
| Message-id: <1381458999-8048-1-git-send-email-asias@redhat.com> |
| O-Subject: [RHEL7.0 qemu-kvm EMBARGOED PATCH v2] scsi: Allocate SCSITargetReq r->buf dynamically [CVE-2013-4344] |
| Bugzilla: 1007334 |
| RH-Acked-by: Kevin Wolf <kwolf@redhat.com> |
| RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com> |
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> |
| |
| r->buf is hardcoded to 2056 which is (256 + 1) * 8, allowing 256 luns at |
| most. If more than 256 luns are specified by user, we have buffer |
| overflow in scsi_target_emulate_report_luns. |
| |
| To fix, we allocate the buffer dynamically. |
| |
| Signed-off-by: Asias He <asias@redhat.com> |
| Tested-by: Michael Roth <mdroth@linux.vnet.ibm.com> |
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> |
| (cherry picked from commit 846424350b292f16b732b573273a5c1f195cd7a3) |
| |
| hw/scsi/scsi-bus.c | 45 ++++++++++++++++++++++++++++++++++----------- |
| include/hw/scsi/scsi.h | 2 ++ |
| 2 files changed, 36 insertions(+), 11 deletions(-) |
| |
| diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c |
| index 18a0399..9082ea7 100644 |
| |
| |
| @@ -11,6 +11,8 @@ static char *scsibus_get_dev_path(DeviceState *dev); |
| static char *scsibus_get_fw_dev_path(DeviceState *dev); |
| static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf); |
| static void scsi_req_dequeue(SCSIRequest *req); |
| +static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len); |
| +static void scsi_target_free_buf(SCSIRequest *req); |
| |
| static Property scsi_props[] = { |
| DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0), |
| @@ -309,7 +311,8 @@ typedef struct SCSITargetReq SCSITargetReq; |
| struct SCSITargetReq { |
| SCSIRequest req; |
| int len; |
| - uint8_t buf[2056]; |
| + uint8_t *buf; |
| + int buf_len; |
| }; |
| |
| static void store_lun(uint8_t *outbuf, int lun) |
| @@ -353,14 +356,12 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r) |
| if (!found_lun0) { |
| n += 8; |
| } |
| - len = MIN(n + 8, r->req.cmd.xfer & ~7); |
| - if (len > sizeof(r->buf)) { |
| - /* TODO: > 256 LUNs? */ |
| - return false; |
| - } |
| |
| + scsi_target_alloc_buf(&r->req, n + 8); |
| + |
| + len = MIN(n + 8, r->req.cmd.xfer & ~7); |
| memset(r->buf, 0, len); |
| - stl_be_p(&r->buf, n); |
| + stl_be_p(&r->buf[0], n); |
| i = found_lun0 ? 8 : 16; |
| QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) { |
| DeviceState *qdev = kid->child; |
| @@ -379,6 +380,9 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r) |
| static bool scsi_target_emulate_inquiry(SCSITargetReq *r) |
| { |
| assert(r->req.dev->lun != r->req.lun); |
| + |
| + scsi_target_alloc_buf(&r->req, SCSI_INQUIRY_LEN); |
| + |
| if (r->req.cmd.buf[1] & 0x2) { |
| /* Command support data - optional, not implemented */ |
| return false; |
| @@ -403,7 +407,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r) |
| return false; |
| } |
| /* done with EVPD */ |
| - assert(r->len < sizeof(r->buf)); |
| + assert(r->len < r->buf_len); |
| r->len = MIN(r->req.cmd.xfer, r->len); |
| return true; |
| } |
| @@ -414,7 +418,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r) |
| } |
| |
| /* PAGE CODE == 0 */ |
| - r->len = MIN(r->req.cmd.xfer, 36); |
| + r->len = MIN(r->req.cmd.xfer, SCSI_INQUIRY_LEN); |
| memset(r->buf, 0, r->len); |
| if (r->req.lun != 0) { |
| r->buf[0] = TYPE_NO_LUN; |
| @@ -447,8 +451,9 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf) |
| } |
| break; |
| case REQUEST_SENSE: |
| + scsi_target_alloc_buf(&r->req, SCSI_SENSE_LEN); |
| r->len = scsi_device_get_sense(r->req.dev, r->buf, |
| - MIN(req->cmd.xfer, sizeof r->buf), |
| + MIN(req->cmd.xfer, r->buf_len), |
| (req->cmd.buf[1] & 1) == 0); |
| if (r->req.dev->sense_is_ua) { |
| scsi_device_unit_attention_reported(req->dev); |
| @@ -493,11 +498,29 @@ static uint8_t *scsi_target_get_buf(SCSIRequest *req) |
| return r->buf; |
| } |
| |
| +static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len) |
| +{ |
| + SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); |
| + |
| + r->buf = g_malloc(len); |
| + r->buf_len = len; |
| + |
| + return r->buf; |
| +} |
| + |
| +static void scsi_target_free_buf(SCSIRequest *req) |
| +{ |
| + SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); |
| + |
| + g_free(r->buf); |
| +} |
| + |
| static const struct SCSIReqOps reqops_target_command = { |
| .size = sizeof(SCSITargetReq), |
| .send_command = scsi_target_send_command, |
| .read_data = scsi_target_read_data, |
| .get_buf = scsi_target_get_buf, |
| + .free_req = scsi_target_free_buf, |
| }; |
| |
| |
| @@ -1353,7 +1376,7 @@ int scsi_build_sense(uint8_t *in_buf, int in_len, |
| buf[7] = 10; |
| buf[12] = sense.asc; |
| buf[13] = sense.ascq; |
| - return MIN(len, 18); |
| + return MIN(len, SCSI_SENSE_LEN); |
| } else { |
| /* Return descriptor format sense buffer */ |
| buf[0] = 0x72; |
| diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h |
| index 9786e00..2eccb72 100644 |
| |
| |
| @@ -9,6 +9,8 @@ |
| #define MAX_SCSI_DEVS 255 |
| |
| #define SCSI_CMD_BUF_SIZE 16 |
| +#define SCSI_SENSE_LEN 18 |
| +#define SCSI_INQUIRY_LEN 36 |
| |
| typedef struct SCSIBus SCSIBus; |
| typedef struct SCSIBusInfo SCSIBusInfo; |
| -- |
| 1.7.1 |
| |