diff options
| author | Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> | 2026-07-31 03:31:44 +0300 |
|---|---|---|
| committer | Bryan O'Donoghue <bod@kernel.org> | 2026-09-29 12:53:47 +0100 |
| commit | af5f7df6c8b33bed714ec0aea15a0a2f48900c0a (patch) | |
| tree | f4049e81a4d9c261c824cf4b03b74cd36ce07da8 /drivers/media | |
| parent | 397ebadcbc219f6c20d11c0378d8cb7695d015c2 (diff) | |
media: iris: reference count video instances
iris_get_instance() looks up an instance on core->instances and returns
it after dropping core->lock, without taking any reference. The threaded
interrupt handler uses this to find the instance a firmware response
belongs to and then takes inst->lock. Meanwhile userspace may close the
same file descriptor: iris_close() removes the instance from the list,
destroys inst->lock and frees the instance. The interrupt handler then
operates on freed memory and a destroyed mutex, a use-after-free.
Add a kref to struct iris_inst. iris_get_instance() takes a reference
under core->lock, so an instance it returns cannot be freed until the
caller drops that reference with iris_inst_put(). The instance is
released (mutexes destroyed, memory freed) only when the last reference
goes away, whether that is held by the closing thread or the interrupt
handler.
Fixes: 38fc8beaba55 ("media: iris: implement reqbuf ioctl with vb2_queue_setup")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Bryan O'Donoghue <bod@kernel.org>
Diffstat (limited to 'drivers/media')
6 files changed, 32 insertions, 5 deletions
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c index 7ad6f0bb4677..ffa98139f0eb 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c @@ -710,6 +710,7 @@ static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response mutex_lock(&inst->lock); iris_hfi_gen1_session_event_notify(inst, hdr); mutex_unlock(&inst->lock); + iris_inst_put(inst); } else { iris_hfi_gen1_sys_event_notify(core, hdr); } @@ -747,6 +748,7 @@ static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response } } mutex_unlock(&inst->lock); + iris_inst_put(inst); break; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c index 8c2644c7f6e8..f0782c4b1e6e 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c @@ -969,6 +969,7 @@ static int iris_hfi_gen2_handle_session_response(struct iris_core *core, iris_hfi_gen2_handle_dequeue_buffers(inst); mutex_unlock(&inst->lock); + iris_inst_put(inst); return ret; } diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index ffdbbd20901a..771269e705d9 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -6,6 +6,8 @@ #ifndef __IRIS_INSTANCE_H__ #define __IRIS_INSTANCE_H__ +#include <linux/kref.h> + #include <media/v4l2-ctrls.h> #include "iris_buffer.h" @@ -21,6 +23,7 @@ struct iris_hfi_session_ops; * struct iris_inst - holds per video instance parameters * * @list: used for attach an instance to the core + * @kref: reference count, keeps the instance alive while the IRQ thread uses it * @core: pointer to core structure * @session_id: id of current video session * @hfi_session_ops: iris HFI session ops @@ -68,6 +71,7 @@ struct iris_hfi_session_ops; struct iris_inst { struct list_head list; + struct kref kref; struct iris_core *core; u32 session_id; const struct iris_hfi_session_ops *hfi_session_ops; diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c index ba5c8dc1280c..1096cc4b01c3 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.c +++ b/drivers/media/platform/qcom/iris/iris_utils.c @@ -92,6 +92,12 @@ struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id) mutex_lock(&core->lock); list_for_each_entry(inst, &core->instances, list) { if (inst->session_id == session_id) { + /* + * Take a reference under core->lock, paired with + * iris_inst_put() once the caller is done, so the + * instance cannot be freed by a concurrent close(). + */ + kref_get(&inst->kref); mutex_unlock(&core->lock); return inst; } diff --git a/drivers/media/platform/qcom/iris/iris_utils.h b/drivers/media/platform/qcom/iris/iris_utils.h index 228a5f963812..be23acc0e848 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.h +++ b/drivers/media/platform/qcom/iris/iris_utils.h @@ -48,6 +48,7 @@ bool iris_split_mode_enabled(struct iris_inst *inst); bool iris_fmt_is_8bit(u32 pixelformat); bool iris_fmt_is_10bit(u32 pixelformat); struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id); +void iris_inst_put(struct iris_inst *inst); void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type, enum vb2_buffer_state state); int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush); diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index 2ce73930017f..05aab6e3005d 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -40,6 +40,22 @@ static void iris_v4l2_fh_deinit(struct iris_inst *inst, struct file *filp) v4l2_fh_exit(&inst->fh); } +static void iris_inst_release(struct kref *kref) +{ + struct iris_inst *inst = container_of(kref, struct iris_inst, kref); + + mutex_destroy(&inst->ctx_q_lock); + mutex_destroy(&inst->lock); + kfree(inst->fmt_src); + kfree(inst->fmt_dst); + kfree(inst); +} + +void iris_inst_put(struct iris_inst *inst) +{ + kref_put(&inst->kref, iris_inst_release); +} + static int iris_add_session(struct iris_inst *inst) { struct iris_core *core = inst->core; @@ -167,6 +183,7 @@ int iris_open(struct file *filp) inst->domain = session_type; inst->session_id = hash32_ptr(inst); inst->state = IRIS_INST_DEINIT; + kref_init(&inst->kref); mutex_init(&inst->lock); mutex_init(&inst->ctx_q_lock); @@ -306,11 +323,7 @@ int iris_close(struct file *filp) iris_check_num_queued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); iris_remove_session(inst); mutex_unlock(&inst->lock); - mutex_destroy(&inst->ctx_q_lock); - mutex_destroy(&inst->lock); - kfree(inst->fmt_src); - kfree(inst->fmt_dst); - kfree(inst); + iris_inst_put(inst); return 0; } |
