Commit 5043348a authored by Anton Vasilyev's avatar Anton Vasilyev Committed by Gerd Hoffmann

drm: qxl: Fix error handling at qxl_device_init

If qxl_device_init fails on creating resources and does not report it,
then qxl module will catch null pointer exception on remove, or on
probe's error path.

The patch adds error path with resources release into qxl_device_init.

Found by Linux Driver Verification project (linuxtesting.org).
Signed-off-by: default avatarAnton Vasilyev <vasilyev@ispras.ru>
Link: http://patchwork.freedesktop.org/patch/msgid/20180727115440.11112-1-vasilyev@ispras.ruSigned-off-by: default avatarGerd Hoffmann <kraxel@redhat.com>
parent b0d146ac
......@@ -102,8 +102,10 @@ int qxl_device_init(struct qxl_device *qdev,
int r, sb;
r = drm_dev_init(&qdev->ddev, drv, &pdev->dev);
if (r)
return r;
if (r) {
pr_err("Unable to init drm dev");
goto error;
}
qdev->ddev.pdev = pdev;
pci_set_drvdata(pdev, &qdev->ddev);
......@@ -121,6 +123,11 @@ int qxl_device_init(struct qxl_device *qdev,
qdev->io_base = pci_resource_start(pdev, 3);
qdev->vram_mapping = io_mapping_create_wc(qdev->vram_base, pci_resource_len(pdev, 0));
if (!qdev->vram_mapping) {
pr_err("Unable to create vram_mapping");
r = -ENOMEM;
goto error;
}
if (pci_resource_len(pdev, 4) > 0) {
/* 64bit surface bar present */
......@@ -139,6 +146,11 @@ int qxl_device_init(struct qxl_device *qdev,
qdev->surface_mapping =
io_mapping_create_wc(qdev->surfaceram_base,
qdev->surfaceram_size);
if (!qdev->surface_mapping) {
pr_err("Unable to create surface_mapping");
r = -ENOMEM;
goto vram_mapping_free;
}
}
DRM_DEBUG_KMS("qxl: vram %llx-%llx(%dM %dk), surface %llx-%llx(%dM %dk, %s)\n",
......@@ -155,20 +167,29 @@ int qxl_device_init(struct qxl_device *qdev,
qdev->rom = ioremap(qdev->rom_base, qdev->rom_size);
if (!qdev->rom) {
pr_err("Unable to ioremap ROM\n");
return -ENOMEM;
r = -ENOMEM;
goto surface_mapping_free;
}
qxl_check_device(qdev);
if (!qxl_check_device(qdev)) {
r = -ENODEV;
goto surface_mapping_free;
}
r = qxl_bo_init(qdev);
if (r) {
DRM_ERROR("bo init failed %d\n", r);
return r;
goto rom_unmap;
}
qdev->ram_header = ioremap(qdev->vram_base +
qdev->rom->ram_header_offset,
sizeof(*qdev->ram_header));
if (!qdev->ram_header) {
DRM_ERROR("Unable to ioremap RAM header\n");
r = -ENOMEM;
goto bo_fini;
}
qdev->command_ring = qxl_ring_create(&(qdev->ram_header->cmd_ring_hdr),
sizeof(struct qxl_command),
......@@ -176,6 +197,11 @@ int qxl_device_init(struct qxl_device *qdev,
qdev->io_base + QXL_IO_NOTIFY_CMD,
false,
&qdev->display_event);
if (!qdev->command_ring) {
DRM_ERROR("Unable to create command ring\n");
r = -ENOMEM;
goto ram_header_unmap;
}
qdev->cursor_ring = qxl_ring_create(
&(qdev->ram_header->cursor_ring_hdr),
......@@ -185,12 +211,23 @@ int qxl_device_init(struct qxl_device *qdev,
false,
&qdev->cursor_event);
if (!qdev->cursor_ring) {
DRM_ERROR("Unable to create cursor ring\n");
r = -ENOMEM;
goto command_ring_free;
}
qdev->release_ring = qxl_ring_create(
&(qdev->ram_header->release_ring_hdr),
sizeof(uint64_t),
QXL_RELEASE_RING_SIZE, 0, true,
NULL);
if (!qdev->release_ring) {
DRM_ERROR("Unable to create release ring\n");
r = -ENOMEM;
goto cursor_ring_free;
}
/* TODO - slot initialization should happen on reset. where is our
* reset handler? */
qdev->n_mem_slots = qdev->rom->slots_end;
......@@ -203,6 +240,12 @@ int qxl_device_init(struct qxl_device *qdev,
kmalloc_array(qdev->n_mem_slots, sizeof(struct qxl_memslot),
GFP_KERNEL);
if (!qdev->mem_slots) {
DRM_ERROR("Unable to alloc mem slots\n");
r = -ENOMEM;
goto release_ring_free;
}
idr_init(&qdev->release_idr);
spin_lock_init(&qdev->release_idr_lock);
spin_lock_init(&qdev->release_lock);
......@@ -218,8 +261,10 @@ int qxl_device_init(struct qxl_device *qdev,
/* must initialize irq before first async io - slot creation */
r = qxl_irq_init(qdev);
if (r)
return r;
if (r) {
DRM_ERROR("Unable to init qxl irq\n");
goto mem_slots_free;
}
/*
* Note that virtual is surface0. We rely on the single ioremap done
......@@ -243,6 +288,27 @@ int qxl_device_init(struct qxl_device *qdev,
INIT_WORK(&qdev->gc_work, qxl_gc_work);
return 0;
mem_slots_free:
kfree(qdev->mem_slots);
release_ring_free:
qxl_ring_free(qdev->release_ring);
cursor_ring_free:
qxl_ring_free(qdev->cursor_ring);
command_ring_free:
qxl_ring_free(qdev->command_ring);
ram_header_unmap:
iounmap(qdev->ram_header);
bo_fini:
qxl_bo_fini(qdev);
rom_unmap:
iounmap(qdev->rom);
surface_mapping_free:
io_mapping_free(qdev->surface_mapping);
vram_mapping_free:
io_mapping_free(qdev->vram_mapping);
error:
return r;
}
void qxl_device_fini(struct qxl_device *qdev)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment