[PATCH 2/2] media: qcom: camss: vfe: Fix registration sequencing bug

From: Bryan O'Donoghue
Date: Thu Jun 12 2025 - 04:08:23 EST


msm_vfe_register_entities loops through each Raw Data Interface input line.
For each loop we add video device with its associated pads.

Once a single /dev/video0 node has been populated it is possible for
camss_find_sensor_pad to run. This routine scans through a list of media
entities taking a pointer pad = media_entity->pad[0] and assuming that
pointer is always valid.

It is possible for both the enumeration loop in msm_vfe_register_entities()
and a call from user-space to run concurrently.

Adding some deliberate sleep code into the loop in
msm_vfe_register_entities() and constructing a user-space program to open
every /dev/videoX node in a tight continuous loop, quickly shows the
following error.

[ 691.074558] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000030
[ 691.074933] Call trace:
[ 691.074935] camss_find_sensor_pad+0x74/0x114 [qcom_camss] (P)
[ 691.074946] camss_get_pixel_clock+0x18/0x64 [qcom_camss]
[ 691.074956] vfe_get+0xc0/0x54c [qcom_camss]
[ 691.074968] vfe_set_power+0x58/0xf4c [qcom_camss]
[ 691.074978] pipeline_pm_power_one+0x124/0x140 [videodev]
[ 691.074986] pipeline_pm_power+0x70/0x100 [videodev]
[ 691.074992] v4l2_pipeline_pm_use+0x54/0x90 [videodev]
[ 691.074998] v4l2_pipeline_pm_get+0x14/0x20 [videodev]
[ 691.075005] video_open+0x74/0xe0 [qcom_camss]
[ 691.075014] v4l2_open+0xa8/0x124 [videodev]
[ 691.075021] chrdev_open+0xb0/0x21c
[ 691.075031] do_dentry_open+0x138/0x4c4
[ 691.075040] vfs_open+0x2c/0xe8
[ 691.075044] path_openat+0x6f0/0x10a0
[ 691.075050] do_filp_open+0xa8/0x164
[ 691.075054] do_sys_openat2+0x94/0x104
[ 691.075058] __arm64_sys_openat+0x64/0xc0
[ 691.075061] invoke_syscall+0x48/0x104
[ 691.075069] el0_svc_common.constprop.0+0x40/0xe0
[ 691.075075] do_el0_svc+0x1c/0x28
[ 691.075080] el0_svc+0x30/0xcc
[ 691.075085] el0t_64_sync_handler+0x10c/0x138
[ 691.075088] el0t_64_sync+0x198/0x19c

Taking the vfe->power_lock is not possible since
v4l2_device_register_subdev takes the mdev->graph_lock. Later on fops->open
takes the mdev->graph_lock followed by vfe_get() -> taking vfe->power_lock.

Introduce a simple enumeration_complete bool which is false initially and
only set true once in our init routine after we complete enumeration.

If user-space tries to interact with the VFE before complete enumeration it
will receive -EAGAIN.

Cc: stable@xxxxxxxxxxxxxxx
Fixes: 4c98a5f57f90 ("media: camss: Add VFE files")
Reported-by: Johan Hovold <johan+linaro@xxxxxxxxxx>
Closes: https://lore.kernel.org/all/Zwjw6XfVWcufMlqM@xxxxxxxxxxxxxxxxxxxx
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@xxxxxxxxxx>
---
drivers/media/platform/qcom/camss/camss-vfe.c | 8 ++++++++
drivers/media/platform/qcom/camss/camss-vfe.h | 1 +
2 files changed, 9 insertions(+)

diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index ac3a9579e3e6910eee8c1ec11c4fff6e1bc94443..3fccc83ebbcc84c20e17da72c359de3dfd18fb40 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -1062,6 +1062,9 @@ int vfe_get(struct vfe_device *vfe)
{
int ret;

+ if (!vfe->enumeration_complete)
+ return -EAGAIN;
+
mutex_lock(&vfe->power_lock);

if (vfe->power_count == 0) {
@@ -1122,6 +1125,9 @@ int vfe_get(struct vfe_device *vfe)
*/
void vfe_put(struct vfe_device *vfe)
{
+ if (!vfe->enumeration_complete)
+ return;
+
mutex_lock(&vfe->power_lock);

if (vfe->power_count == 0) {
@@ -2091,6 +2097,8 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
}
}

+ vfe->enumeration_complete = true;
+
return 0;

error_link:
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h
index 614e932c33da78e02e0800ce6534af7b14822f83..33b59dcfc8b2b81e896336b079a41eba603ec400 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.h
+++ b/drivers/media/platform/qcom/camss/camss-vfe.h
@@ -169,6 +169,7 @@ struct vfe_device {
struct camss_video_ops video_ops;
struct device *genpd;
struct device_link *genpd_link;
+ bool enumeration_complete;
};

struct camss_subdev_resources;

--
2.49.0