From: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
To: Vikash Garodia <vikash.garodia@oss.qualcomm.com>,
Dikshita Agarwal <dikshita.agarwal@oss.qualcomm.com>,
Abhinav Kumar <abhinav.kumar@linux.dev>,
Bryan O'Donoghue <bod@kernel.org>,
Mauro Carvalho Chehab <mchehab@kernel.org>,
Hans Verkuil <hverkuil@kernel.org>,
Stefan Schmidt <stefan.schmidt@linaro.org>,
Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>,
Vishnu Reddy <busanna.reddy@oss.qualcomm.com>,
Hungyu Lin <dennylin0707@gmail.com>
Cc: linux-media@vger.kernel.org, linux-arm-msm@vger.kernel.org,
linux-kernel@vger.kernel.org,
Bryan O'Donoghue <bryan.odonoghue@linaro.org>,
sashiko-bot <sashiko-bot@kernel.org>,
stable@vger.kernel.org
Subject: [PATCH v2 3/5] media: iris: Fix use-after-free on unbind with open sessions
Date: Tue, 10 Nov 2026 19:39:51 +0530 [thread overview]
Message-ID: <20261110-iris-handle-unbind-v2-3-86d5f6f1dea8@oss.qualcomm.com> (raw)
In-Reply-To: <20261110-iris-handle-unbind-v2-0-86d5f6f1dea8@oss.qualcomm.com>
The driver can be unbound through sysfs while a client has a video
session open. The core is devm allocated and freed when remove()
returns, so the open sessions then use freed memory.
Only the core structure needs to outlive remove(), because it is the
only thing the file operations of an open session still use. Allocate
it with kzalloc and tie it to the reference count of its v4l2_device,
which every registered video device holds. It is freed when the last
file descriptor is closed. The other devm resources (register mapping,
response packet, clocks, power domains, interconnects) are only used by
the power on/off sequence, the clock and bandwidth votes and the
interrupt handler. All of them are released with the unbind, after the
core is shut down.
To make sure nothing uses them afterwards, set a removing flag at the
start of iris_remove(). It makes core init, new sessions, command
writes and the clock and interconnect votes fail with -ENODEV, so a
client still holding a file descriptor cannot power the hardware on or
vote for resources that are gone. A client waiting for the firmware
only times out and returns an error without touching them. Runtime PM
is also disabled explicitly in iris_remove() instead of with devm,
because its callbacks use the core.
Fixes: 38506cb7e8d2 ("media: iris: add platform driver for iris video device")
Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260926064921.976721F000FF@smtp.kernel.org
Cc: stable@vger.kernel.org
Signed-off-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
---
drivers/media/platform/qcom/iris/iris_core.c | 10 +++++-
drivers/media/platform/qcom/iris/iris_core.h | 2 ++
drivers/media/platform/qcom/iris/iris_hfi_queue.c | 15 +++++++-
drivers/media/platform/qcom/iris/iris_power.c | 13 +++++++
drivers/media/platform/qcom/iris/iris_probe.c | 44 ++++++++++++++++++-----
drivers/media/platform/qcom/iris/iris_vidc.c | 3 ++
6 files changed, 76 insertions(+), 11 deletions(-)
diff --git a/drivers/media/platform/qcom/iris/iris_core.c b/drivers/media/platform/qcom/iris/iris_core.c
index d558475986ff0..bdac6b8c98e44 100644
--- a/drivers/media/platform/qcom/iris/iris_core.c
+++ b/drivers/media/platform/qcom/iris/iris_core.c
@@ -43,7 +43,10 @@ static int iris_wait_for_system_response(struct iris_core *core)
ret = wait_for_completion_timeout(&core->core_init_done,
msecs_to_jiffies(HW_RESPONSE_TIMEOUT_VALUE));
if (!ret) {
- core->state = IRIS_CORE_ERROR;
+ mutex_lock(&core->lock);
+ if (!core->removing)
+ core->state = IRIS_CORE_ERROR;
+ mutex_unlock(&core->lock);
return -ETIMEDOUT;
}
@@ -56,6 +59,11 @@ int iris_core_init(struct iris_core *core)
int ret;
mutex_lock(&core->lock);
+ if (core->removing) {
+ ret = -ENODEV;
+ goto exit;
+ }
+
if (core->state == IRIS_CORE_INIT) {
ret = 0;
goto exit;
diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h
index 3e4cf652bf39a..379f0666a42cb 100644
--- a/drivers/media/platform/qcom/iris/iris_core.h
+++ b/drivers/media/platform/qcom/iris/iris_core.h
@@ -62,6 +62,7 @@ struct qcom_ubwc_cfg_data;
* @iris_firmware_desc: a pointer to the firmware-specific descriptive data
* @ubwc_cfg: UBWC configuration for the platform
* @state: current state of core
+ * @removing: set when driver removal starts, blocks core init and resource votes
* @iface_q_table_daddr: device address for interface queue table memory
* @sfr_daddr: device address for SFR (Sub System Failure Reason) register memory
* @iface_q_table_vaddr: virtual address for interface queue table memory
@@ -108,6 +109,7 @@ struct iris_core {
const struct iris_firmware_desc *iris_firmware_desc;
const struct qcom_ubwc_cfg_data *ubwc_cfg;
enum iris_core_state state;
+ bool removing;
dma_addr_t iface_q_table_daddr;
dma_addr_t sfr_daddr;
void *iface_q_table_vaddr;
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_queue.c b/drivers/media/platform/qcom/iris/iris_hfi_queue.c
index 545bbe75e1740..e0cfa5107d1d9 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_queue.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_queue.c
@@ -128,13 +128,26 @@ int iris_hfi_queue_cmd_write_locked(struct iris_core *core, void *pkt, u32 pkt_s
int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt, u32 pkt_size)
{
- int ret;
+ int ret = 0;
+
+ mutex_lock(&core->lock);
+ if (core->removing)
+ ret = -ENODEV;
+ mutex_unlock(&core->lock);
+ if (ret)
+ return ret;
ret = pm_runtime_resume_and_get(core->dev);
if (ret < 0)
return ret;
mutex_lock(&core->lock);
+ if (core->removing) {
+ mutex_unlock(&core->lock);
+ ret = -ENODEV;
+ goto exit;
+ }
+
ret = iris_hfi_queue_cmd_write_locked(core, pkt, pkt_size);
if (ret) {
mutex_unlock(&core->lock);
diff --git a/drivers/media/platform/qcom/iris/iris_power.c b/drivers/media/platform/qcom/iris/iris_power.c
index 91aa21d4070eb..1f2a7e1bb9e1f 100644
--- a/drivers/media/platform/qcom/iris/iris_power.c
+++ b/drivers/media/platform/qcom/iris/iris_power.c
@@ -47,6 +47,11 @@ static int iris_set_interconnects(struct iris_inst *inst)
int ret;
mutex_lock(&core->lock);
+ if (core->removing) {
+ ret = -ENODEV;
+ goto exit;
+ }
+
list_for_each_entry(instance, &core->instances, list) {
if (!instance->max_input_data_size)
continue;
@@ -56,6 +61,7 @@ static int iris_set_interconnects(struct iris_inst *inst)
ret = iris_set_icc_bw(core, total_bw_ddr);
+exit:
mutex_unlock(&core->lock);
return ret;
@@ -83,6 +89,11 @@ static int iris_set_clocks(struct iris_inst *inst)
int ret;
mutex_lock(&core->lock);
+ if (core->removing) {
+ ret = -ENODEV;
+ goto exit;
+ }
+
list_for_each_entry(instance, &core->instances, list) {
if (!instance->max_input_data_size)
continue;
@@ -92,6 +103,8 @@ static int iris_set_clocks(struct iris_inst *inst)
core->power.clk_freq = freq;
ret = iris_opp_set_rate(core->dev, freq);
+
+exit:
mutex_unlock(&core->lock);
return ret;
diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c
index 6f02bb629867b..dc83daccea5f7 100644
--- a/drivers/media/platform/qcom/iris/iris_probe.c
+++ b/drivers/media/platform/qcom/iris/iris_probe.c
@@ -243,6 +243,22 @@ static int iris_register_video_device(struct iris_core *core, enum domain_type t
return ret;
}
+static void iris_core_release(struct v4l2_device *v4l2_dev)
+{
+ struct iris_core *core = container_of(v4l2_dev, struct iris_core, v4l2_dev);
+
+ mutex_destroy(&core->lock);
+ put_device(core->dev);
+ kfree(core);
+}
+
+static void iris_core_put(void *data)
+{
+ struct iris_core *core = data;
+
+ v4l2_device_put(&core->v4l2_dev);
+}
+
static void iris_remove(struct platform_device *pdev)
{
struct iris_core *core;
@@ -251,18 +267,23 @@ static void iris_remove(struct platform_device *pdev)
if (!core)
return;
+ mutex_lock(&core->lock);
+ core->removing = true;
+ mutex_unlock(&core->lock);
+
+ video_unregister_device(core->vdev_dec);
+ video_unregister_device(core->vdev_enc);
+
disable_delayed_work_sync(&core->sys_error_handler);
iris_core_deinit(core);
- video_unregister_device(core->vdev_dec);
- video_unregister_device(core->vdev_enc);
+ pm_runtime_dont_use_autosuspend(core->dev);
+ pm_runtime_disable(core->dev);
v4l2_device_unregister(&core->v4l2_dev);
iris_unreserve_iova_region(core->dev, core->iova_state);
-
- mutex_destroy(&core->lock);
}
static void iris_sys_error_handler(struct work_struct *work)
@@ -281,15 +302,22 @@ static int iris_probe(struct platform_device *pdev)
u64 dma_mask;
int ret;
- core = devm_kzalloc_obj(&pdev->dev, *core);
+ core = kzalloc_obj(*core);
if (!core)
return -ENOMEM;
- core->dev = dev;
+ core->dev = get_device(dev);
+
+ core->v4l2_dev.release = iris_core_release;
+ kref_init(&core->v4l2_dev.ref);
core->state = IRIS_CORE_DEINIT;
mutex_init(&core->lock);
init_completion(&core->core_init_done);
+ ret = devm_add_action_or_reset(dev, iris_core_put, core);
+ if (ret)
+ return ret;
+
core->response_packet = devm_kzalloc(core->dev, IFACEQ_CORE_PKT_SIZE, GFP_KERNEL);
if (!core->response_packet)
return -ENOMEM;
@@ -354,9 +382,7 @@ static int iris_probe(struct platform_device *pdev)
pm_runtime_set_autosuspend_delay(core->dev, AUTOSUSPEND_DELAY_VALUE);
pm_runtime_use_autosuspend(core->dev);
- ret = devm_pm_runtime_enable(core->dev);
- if (ret)
- goto err_vdev_unreg_enc;
+ pm_runtime_enable(core->dev);
return 0;
diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c
index 05aab6e3005d5..04bd37b934457 100644
--- a/drivers/media/platform/qcom/iris/iris_vidc.c
+++ b/drivers/media/platform/qcom/iris/iris_vidc.c
@@ -64,6 +64,9 @@ static int iris_add_session(struct iris_inst *inst)
guard(mutex)(&core->lock);
+ if (core->removing)
+ return -ENODEV;
+
list_for_each_entry(iter, &core->instances, list)
count++;
--
2.34.1
next prev parent reply other threads:[~2026-11-10 14:11 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 14:09 [PATCH v2 0/5] media: iris: Fix issues in the driver unbind path Vishnu Reddy
2026-11-10 14:09 ` [PATCH v2 1/5] media: iris: Fix runtime PM reference imbalance on failed resume Vishnu Reddy
2026-11-10 14:09 ` [PATCH v2 2/5] media: iris: Disable the system error work on removal Vishnu Reddy
2026-11-10 14:09 ` Vishnu Reddy [this message]
2026-11-10 14:42 ` [PATCH v2 3/5] media: iris: Fix use-after-free on unbind with open sessions Bryan O'Donoghue
2026-11-10 14:09 ` [PATCH v2 4/5] media: iris: Wake up blocked clients when the driver is removed Vishnu Reddy
2026-11-10 14:09 ` [PATCH v2 5/5] media: iris: Free the interrupt in iris_remove() Vishnu Reddy
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261110-iris-handle-unbind-v2-3-86d5f6f1dea8@oss.qualcomm.com \
--to=busanna.reddy@oss.qualcomm.com \
--cc=abhinav.kumar@linux.dev \
--cc=bod@kernel.org \
--cc=bryan.odonoghue@linaro.org \
--cc=dennylin0707@gmail.com \
--cc=dikshita.agarwal@oss.qualcomm.com \
--cc=dmitry.baryshkov@oss.qualcomm.com \
--cc=hverkuil@kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=mchehab@kernel.org \
--cc=sashiko-bot@kernel.org \
--cc=stable@vger.kernel.org \
--cc=stefan.schmidt@linaro.org \
--cc=vikash.garodia@oss.qualcomm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®