msm: vidc: Fix use after free in driver

Use after free can happen in below scenario.
[1] In msm_vidc_open, When instance count reaches max count or when
open failed it waits for core lock before removing instance from
instance list.
[2] During this time backward thread can get invoked to handle firmware
response for cmds, It increments reference count and gets instance.
[3] If forward thread resumes after this, and frees intance,
backward thread will end up using freed instance.
[4] To address this, calling kref_put(&inst->kref) and freeing instance
immediately if ref count is zero, if recount is not zero, backward
thread frees instance memory in put_inst_helper.

Change-Id: I58c81d4d9fda1523d0fa29ebdb33455db839d46f
Signed-off-by: Vasantha Balla <quic_c_vballa@quicinc.com>
(cherry picked from commit ccddad0c4992ab7b4e3651e2c5c8df3e79bb7c43)
(cherry picked from commit cc9f3cc50311796626b2c40b049daeef0a5719ea)
This commit is contained in:
Vasantha Balla 2025-03-28 12:16:47 +05:30
commit 3fedfd9225

View file

@ -1482,7 +1482,6 @@ static void close_helper(struct kref *kref)
{
struct msm_vidc_inst *inst = container_of(kref,
struct msm_vidc_inst, kref);
msm_vidc_destroy(inst);
}
@ -1497,19 +1496,19 @@ void *msm_vidc_open(int core_id, int session_type)
session_type >= MSM_VIDC_MAX_DEVICES) {
d_vpr_e("Invalid input, core_id = %d, session = %d\n",
core_id, session_type);
goto err_invalid_core;
return NULL;
}
core = get_vidc_core(core_id);
if (!core) {
d_vpr_e("Failed to find core for core_id = %d\n", core_id);
goto err_invalid_core;
return NULL;
}
inst = kzalloc(sizeof(*inst), GFP_KERNEL);
if (!inst) {
d_vpr_e("Failed to allocate memory\n");
rc = -ENOMEM;
goto err_invalid_core;
return NULL;
}
mutex_lock(&core->lock);
rc = get_sid(&inst->sid, session_type);
@ -1609,14 +1608,15 @@ void *msm_vidc_open(int core_id, int session_type)
s_vpr_e(inst->sid,
"Failed to move video instance to init state\n");
kref_put(&inst->kref, close_helper);
inst = NULL;
goto err_invalid_core;
return NULL;
}
if (msm_comm_check_for_inst_overload(core)) {
s_vpr_e(inst->sid,
"Instance count reached Max limit, rejecting session");
goto fail_init;
msm_comm_kill_session(inst);
kref_put(&inst->kref, close_helper);
return NULL;
}
msm_comm_scale_clocks_and_bus(inst, 1);
@ -1625,14 +1625,6 @@ void *msm_vidc_open(int core_id, int session_type)
msm_vidc_debugfs_init_inst(inst, core->debugfs_root);
return inst;
fail_init:
mutex_lock(&core->lock);
list_del(&inst->list);
mutex_unlock(&core->lock);
v4l2_fh_del(&inst->event_handler);
v4l2_fh_exit(&inst->event_handler);
vb2_queue_release(&inst->bufq[INPUT_PORT].vb2_bufq);
fail_bufq_output:
vb2_queue_release(&inst->bufq[OUTPUT_PORT].vb2_bufq);
fail_bufq_capture:
@ -1660,7 +1652,6 @@ err_invalid_sid:
put_sid(inst->sid);
kfree(inst);
inst = NULL;
err_invalid_core:
return inst;
}
EXPORT_SYMBOL(msm_vidc_open);