diff options
author | Mauro Carvalho Chehab <mchehab+huawei@kernel.org> | 2021-04-08 08:49:12 +0200 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab+huawei@kernel.org> | 2021-04-08 10:04:20 +0200 |
commit | 686ee9b6253f9b6d7f1151e73114698940cc0894 (patch) | |
tree | dda8df182732fa1c21fadee1a7405ef586263f20 /drivers | |
parent | b6f139947e93fec1ade5faf3517dfb2b3b9bcd41 (diff) | |
download | linux-686ee9b6253f9b6d7f1151e73114698940cc0894.tar.bz2 |
media: venus: don't de-reference NULL pointers at IRQ time
Smatch is warning that:
drivers/media/platform/qcom/venus/hfi_venus.c:1100 venus_isr() warn: variable dereferenced before check 'hdev' (see line 1097)
The logic basically does:
hdev = to_hfi_priv(core);
with is translated to:
hdev = core->priv;
If the IRQ code can receive a NULL pointer for hdev, there's
a bug there, as it will first try to de-reference the pointer,
and then check if it is null.
After looking at the code, it seems that this indeed can happen:
Basically, the venus IRQ thread is started with:
devm_request_threaded_irq()
So, it will only be freed after the driver unbinds.
In order to prevent the IRQ code to work with freed data,
the logic at venus_hfi_destroy() sets core->priv to NULL,
which would make the IRQ code to ignore any pending IRQs.
There is, however a race condition, as core->priv is set
to NULL only after being freed. So, we need also to move the
core->priv = NULL to happen earlier.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/platform/qcom/venus/hfi_venus.c | 9 |
1 files changed, 6 insertions, 3 deletions
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index cebb20cf371f..ce98c523b3c6 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -1094,12 +1094,15 @@ static irqreturn_t venus_isr(struct venus_core *core) { struct venus_hfi_device *hdev = to_hfi_priv(core); u32 status; - void __iomem *cpu_cs_base = hdev->core->cpu_cs_base; - void __iomem *wrapper_base = hdev->core->wrapper_base; + void __iomem *cpu_cs_base; + void __iomem *wrapper_base; if (!hdev) return IRQ_NONE; + cpu_cs_base = hdev->core->cpu_cs_base; + wrapper_base = hdev->core->wrapper_base; + status = readl(wrapper_base + WRAPPER_INTR_STATUS); if (IS_V6(core)) { if (status & WRAPPER_INTR_STATUS_A2H_MASK || @@ -1650,10 +1653,10 @@ void venus_hfi_destroy(struct venus_core *core) { struct venus_hfi_device *hdev = to_hfi_priv(core); + core->priv = NULL; venus_interface_queues_release(hdev); mutex_destroy(&hdev->lock); kfree(hdev); - core->priv = NULL; core->ops = NULL; } |