Commit 87e459a8 authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

megasas: always store SCSIRequest* into MegasasCmd



This ensures that the request is unref'ed properly, and avoids a
segmentation fault in the new qtest testcase that is added.
This is CVE-2017-9503.

Reported-by: default avatarZhangyanyu <zyy4013@stu.ouc.edu.cn>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent b356807f
Loading
Loading
Loading
Loading
+16 −15
Original line number Diff line number Diff line
@@ -609,6 +609,9 @@ static void megasas_reset_frames(MegasasState *s)
static void megasas_abort_command(MegasasCmd *cmd)
{
    /* Never abort internal commands.  */
    if (cmd->dcmd_opcode != -1) {
        return;
    }
    if (cmd->req != NULL) {
        scsi_req_cancel(cmd->req);
    }
@@ -1017,7 +1020,6 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
    uint64_t pd_size;
    uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
    uint8_t cmdbuf[6];
    SCSIRequest *req;
    size_t len, resid;

    if (!cmd->iov_buf) {
@@ -1026,8 +1028,8 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
        info->inquiry_data[0] = 0x7f; /* Force PQual 0x3, PType 0x1f */
        info->vpd_page83[0] = 0x7f;
        megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data));
        req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
        if (!req) {
        cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
        if (!cmd->req) {
            trace_megasas_dcmd_req_alloc_failed(cmd->index,
                                                "PD get info std inquiry");
            g_free(cmd->iov_buf);
@@ -1036,26 +1038,26 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
        }
        trace_megasas_dcmd_internal_submit(cmd->index,
                                           "PD get info std inquiry", lun);
        len = scsi_req_enqueue(req);
        len = scsi_req_enqueue(cmd->req);
        if (len > 0) {
            cmd->iov_size = len;
            scsi_req_continue(req);
            scsi_req_continue(cmd->req);
        }
        return MFI_STAT_INVALID_STATUS;
    } else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) {
        megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83));
        req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
        if (!req) {
        cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
        if (!cmd->req) {
            trace_megasas_dcmd_req_alloc_failed(cmd->index,
                                                "PD get info vpd inquiry");
            return MFI_STAT_FLASH_ALLOC_FAIL;
        }
        trace_megasas_dcmd_internal_submit(cmd->index,
                                           "PD get info vpd inquiry", lun);
        len = scsi_req_enqueue(req);
        len = scsi_req_enqueue(cmd->req);
        if (len > 0) {
            cmd->iov_size = len;
            scsi_req_continue(req);
            scsi_req_continue(cmd->req);
        }
        return MFI_STAT_INVALID_STATUS;
    }
@@ -1217,7 +1219,6 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
    struct mfi_ld_info *info = cmd->iov_buf;
    size_t dcmd_size = sizeof(struct mfi_ld_info);
    uint8_t cdb[6];
    SCSIRequest *req;
    ssize_t len, resid;
    uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
    uint64_t ld_size;
@@ -1226,8 +1227,8 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
        cmd->iov_buf = g_malloc0(dcmd_size);
        info = cmd->iov_buf;
        megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83));
        req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd);
        if (!req) {
        cmd->req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd);
        if (!cmd->req) {
            trace_megasas_dcmd_req_alloc_failed(cmd->index,
                                                "LD get info vpd inquiry");
            g_free(cmd->iov_buf);
@@ -1236,10 +1237,10 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
        }
        trace_megasas_dcmd_internal_submit(cmd->index,
                                           "LD get info vpd inquiry", lun);
        len = scsi_req_enqueue(req);
        len = scsi_req_enqueue(cmd->req);
        if (len > 0) {
            cmd->iov_size = len;
            scsi_req_continue(req);
            scsi_req_continue(cmd->req);
        }
        return MFI_STAT_INVALID_STATUS;
    }
@@ -1851,7 +1852,7 @@ static void megasas_command_complete(SCSIRequest *req, uint32_t status,
        return;
    }

    if (cmd->req == NULL) {
    if (cmd->dcmd_opcode != -1) {
        /*
         * Internal command complete
         */
+35 −0
Original line number Diff line number Diff line
@@ -42,10 +42,45 @@ static void pci_nop(void)
    qmegasas_stop(qs);
}

/* This used to cause a NULL pointer dereference.  */
static void megasas_pd_get_info_fuzz(void)
{
    QPCIDevice *dev;
    QOSState *qs;
    QPCIBar bar;
    uint32_t context[256];
    uint64_t context_pa;
    int i;

    qs = qmegasas_start(NULL);
    dev = qpci_device_find(qs->pcibus, QPCI_DEVFN(4,0));
    g_assert(dev != NULL);

    qpci_device_enable(dev);
    bar = qpci_iomap(dev, 0, NULL);

    memset(context, 0, sizeof(context));
    context[0] = cpu_to_le32(0x05050505);
    context[1] = cpu_to_le32(0x01010101);
    for (i = 2; i < ARRAY_SIZE(context); i++) {
        context[i] = cpu_to_le32(0x41414141);
    }
    context[6] = cpu_to_le32(0x02020000);
    context[7] = cpu_to_le32(0);

    context_pa = qmalloc(qs, sizeof(context));
    memwrite(context_pa, context, sizeof(context));
    qpci_io_writel(dev, bar, 0x40, context_pa);

    g_free(dev);
    qmegasas_stop(qs);
}

int main(int argc, char **argv)
{
    g_test_init(&argc, &argv, NULL);
    qtest_add_func("/megasas/pci/nop", pci_nop);
    qtest_add_func("/megasas/dcmd/pd-get-info/fuzz", megasas_pd_get_info_fuzz);

    return g_test_run();
}