Multiple NULL deref on alloc_workqueue

From: Semmle Security Reports
Date: Mon Sep 09 2019 - 11:35:21 EST


There are multiple points in the Linux Kernel where alloc_workqueue is
not getting checked for errors and as a result, a potential NULL
dereference could occur.

I'm attaching a patch for some of them.
There are two cases I left untouched that requires a bit more refactoring:
https://github.com/torvalds/linux/blob/master/drivers/net/wireless/intel/iwlwifi/pcie/trans.c#L3656
https://github.com/torvalds/linux/blob/master/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c#L4508

Regards,
Semmle security team
diff --git a/linux/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c b/b/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c
index c56ac47..252c57e 100644
--- a/linux/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c
+++ b/b/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c
@@ -62,6 +62,11 @@ int kfd_interrupt_init(struct kfd_dev *kfd)
}

kfd->ih_wq = alloc_workqueue("KFD IH", WQ_HIGHPRI, 1);
+ if( !kfd->ih_wq ) {
+ kfifo_free(&kfd->ih_fifo);
+ dev_err(kfd_chardev(), "Failed to allocate KFD IH workqueue\n");
+ return kfd->ih_wq;
+ }
spin_lock_init(&kfd->interrupt_lock);

INIT_WORK(&kfd->interrupt_work, interrupt_wq);
diff --git a/linux/drivers/gpu/drm/radeon/radeon_display.c b/b/drivers/gpu/drm/radeon/radeon_display.c
index bd52f15..1a49030 100644
--- a/linux/drivers/gpu/drm/radeon/radeon_display.c
+++ b/b/drivers/gpu/drm/radeon/radeon_display.c
@@ -683,6 +683,11 @@ static void radeon_crtc_init(struct drm_device *dev, int index)
drm_mode_crtc_set_gamma_size(&radeon_crtc->base, 256);
radeon_crtc->crtc_id = index;
radeon_crtc->flip_queue = alloc_workqueue("radeon-crtc", WQ_HIGHPRI, 0);
+ if( !radeon_crtc->flip_queue) {
+ kfree(radeon_crtc);
+ return;
+
+ }
rdev->mode_info.crtcs[index] = radeon_crtc;

if (rdev->family >= CHIP_BONAIRE) {
diff --git a/linux/drivers/net/fjes/fjes_main.c b/b/drivers/net/fjes/fjes_main.c
index bbbc1dc..d850b17 100644
--- a/linux/drivers/net/fjes/fjes_main.c
+++ b/b/drivers/net/fjes/fjes_main.c
@@ -1237,8 +1237,15 @@ static int fjes_probe(struct platform_device *plat_dev)
adapter->open_guard = false;

adapter->txrx_wq = alloc_workqueue(DRV_NAME "/txrx", WQ_MEM_RECLAIM, 0);
+ if(!adapter->txrx_wq) {
+ goto err_free_netdev;
+ }
adapter->control_wq = alloc_workqueue(DRV_NAME "/control",
WQ_MEM_RECLAIM, 0);
+ if(!adapter->control_wq) {
+ destroy_workqueue(adapter->txrx_wq);
+ goto err_free_netdev;
+ }

INIT_WORK(&adapter->tx_stall_task, fjes_tx_stall_task);
INIT_WORK(&adapter->raise_intr_rxdata_task,
diff --git a/linux/drivers/net/wireless/marvell/libertas/if_sdio.c b/b/drivers/net/wireless/marvell/libertas/if_sdio.c
index 242d884..03083eb 100644
--- a/linux/drivers/net/wireless/marvell/libertas/if_sdio.c
+++ b/b/drivers/net/wireless/marvell/libertas/if_sdio.c
@@ -1179,6 +1179,10 @@ static int if_sdio_probe(struct sdio_func *func,

spin_lock_init(&card->lock);
card->workqueue = alloc_workqueue("libertas_sdio", WQ_MEM_RECLAIM, 0);
+ if(!card->workqueue) {
+ ret = -ENOMEM;
+ goto free_before_queue:;
+ }
INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
init_waitqueue_head(&card->pwron_waitq);

@@ -1230,6 +1234,7 @@ err_activate_card:
lbs_remove_card(priv);
free:
destroy_workqueue(card->workqueue);
+free_before_queue:
while (card->packets) {
packet = card->packets;
card->packets = card->packets->next;
diff --git a/linux/drivers/scsi/qla2xxx/qla_os.c b/b/drivers/scsi/qla2xxx/qla_os.c
index 98e60a3..8f285c5 100644
--- a/linux/drivers/scsi/qla2xxx/qla_os.c
+++ b/b/drivers/scsi/qla2xxx/qla_os.c
@@ -3232,6 +3232,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
req->req_q_in, req->req_q_out, rsp->rsp_q_in, rsp->rsp_q_out);

ha->wq = alloc_workqueue("qla2xxx_wq", 0, 0);
+ if(!ha->wq) {
+ ret = -ENOMEM;
+ goto probe_failed;
+ }

if (ha->isp_ops->initialize_adapter(base_vha)) {
ql_log(ql_log_fatal, base_vha, 0x00d6,