* [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs
@ 2026-07-13 12:06 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk
This patch series addresses a collection of critical race conditions,
memory leaks, use-after-free bugs, and resource management issues
within the Amlogic Meson video decoder (meson-vdec) driver and
associated components.
The primary focus is tightening session lifecycles, safely synchronising
the V4L2 m2m framework callbacks, hardening work queue operations
(esparser_queue_work), and correcting buffer validations to prevent
stability issues like KASAN out-of-bounds errors or stream-on timeouts.
Summary of Changes:
* Race Conditions & Synchronisation: Fixes multi-threading and
execution races across stream-on, stream-off, job abort, and
teardown pathways.
* Memory Lifecycle: Eliminates memory leaks and use-after-free
vulnerabilities by safely pinning, canceling, and sequencing
device removals.
* VP9 & Buffer Hardening: Addresses payload handling bugs in the ES
parser, properly tracks ongoing buffers via atomic counters, and
prevents invalid memory writes.
* Platform/DMA Adjustments: Adjusts DMA segment configurations,
canvas handling, and buffer sizes to match modern hardware demands.
Note on an outstanding issue:
An execution deadlock occurs in the driver workqueue path during high
stress or long playback sessions. This stall is triggered when the
Amlogic hardware internal video FIFO buffer becomes completely full.
When esparser_queue_all_src runs, the capacity validation check is
tripped due to a design limitation in how buffer exhaustion is handled.
This blocks progress and leaves the workqueue worker waiting indefinitely
on a core mutex lock.
The resulting hung task call trace shows the worker thread blocking
on the internal lock:
Workqueue: events esparser_queue_all_src [meson_vdec]
...
__mutex_lock
mutex_lock_nested
esparser_queue_all_src
I have tried to address this issue, but the underlying deadlock persists
under extreme conditions.
Any feedback on this new appock is welcome.
Testing was done using v4l2-compliance and active decoding pipelines
on Meson platforms (like Hardkernel ODROID-N2+ and ODROID-C4) SBCs.
The v4l2-compliance tests passed successfully via:
$ v4l2-compliance -s -v -d /dev/video0
Pipelines were validated using the following GStreamer script.
[1] https://gist.github.com/moonlinux/08295ba1f17ce7155550773f2d2cb6e5
Previous changes:
v6: https://lore.kernel.org/all/20260530094326.11892-2-linux.amoon@gmail.com/
Thanks
-Annad
Anand Moon (19):
media: meson: vdec: Fix m2m device lifetime and cleanup path
media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and
session teardown
media: meson: vdec: Fix lifecycle leaks and race conditions in
recycle_thread
media: meson: vdec: Fix use-after-free race between teardown and ISR
routines
media: meson: vdec: Fix race condition and synchronize esparser IRQ
media: meson: vdec: Fix race condition by canceling work sync
media: meson: vdec: Refactor esparser work queue and fix teardown race
media: meson: vdec: Fix concurrent execution races and unsafe teardown
media: meson: vdec: Fix vp9 header update failure on invalid payloads
media: meson: vdec: Fix race conditions and leaks in esparser pipeline
media: meson: vdec: Update core m2m stream state during transitions
media: meson: vdec: Coordinate m2m task execution inside async loop
media: meson: vdec: Fix race conditions in job abort sequence
media: meson: vdec: Correct atomic counter placement in dst_buf_done
media: meson: vdec: Fix concurrent firmware loading race and hardware
timeout
media: meson: vdec: Configure DMA mask and segment size in probe
media: meson: canvas: Fix Use-After-Free by linking canvas provider
device
media: meson: vdec: Increase VIFIFO buffer size to 32 MiB
gpu: drm: meson: Fix DMA segment size limits and maximize allocation
boundaries
drivers/gpu/drm/meson/meson_drv.c | 6 +
drivers/soc/amlogic/meson-canvas.c | 14 +-
drivers/staging/media/meson/vdec/esparser.c | 127 +++++--
drivers/staging/media/meson/vdec/vdec.c | 320 ++++++++++++++----
drivers/staging/media/meson/vdec/vdec.h | 8 +-
drivers/staging/media/meson/vdec/vdec_1.c | 16 +-
.../staging/media/meson/vdec/vdec_helpers.c | 3 +
7 files changed, 388 insertions(+), 106 deletions(-)
base-commit: a13c140cc289c0b7b3770bce5b3ad42ab35074aa
--
2.50.1
^ permalink raw reply [flat|nested] 102+ messages in thread
* [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs
@ 2026-07-13 12:06 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk
This patch series addresses a collection of critical race conditions,
memory leaks, use-after-free bugs, and resource management issues
within the Amlogic Meson video decoder (meson-vdec) driver and
associated components.
The primary focus is tightening session lifecycles, safely synchronising
the V4L2 m2m framework callbacks, hardening work queue operations
(esparser_queue_work), and correcting buffer validations to prevent
stability issues like KASAN out-of-bounds errors or stream-on timeouts.
Summary of Changes:
* Race Conditions & Synchronisation: Fixes multi-threading and
execution races across stream-on, stream-off, job abort, and
teardown pathways.
* Memory Lifecycle: Eliminates memory leaks and use-after-free
vulnerabilities by safely pinning, canceling, and sequencing
device removals.
* VP9 & Buffer Hardening: Addresses payload handling bugs in the ES
parser, properly tracks ongoing buffers via atomic counters, and
prevents invalid memory writes.
* Platform/DMA Adjustments: Adjusts DMA segment configurations,
canvas handling, and buffer sizes to match modern hardware demands.
Note on an outstanding issue:
An execution deadlock occurs in the driver workqueue path during high
stress or long playback sessions. This stall is triggered when the
Amlogic hardware internal video FIFO buffer becomes completely full.
When esparser_queue_all_src runs, the capacity validation check is
tripped due to a design limitation in how buffer exhaustion is handled.
This blocks progress and leaves the workqueue worker waiting indefinitely
on a core mutex lock.
The resulting hung task call trace shows the worker thread blocking
on the internal lock:
Workqueue: events esparser_queue_all_src [meson_vdec]
...
__mutex_lock
mutex_lock_nested
esparser_queue_all_src
I have tried to address this issue, but the underlying deadlock persists
under extreme conditions.
Any feedback on this new appock is welcome.
Testing was done using v4l2-compliance and active decoding pipelines
on Meson platforms (like Hardkernel ODROID-N2+ and ODROID-C4) SBCs.
The v4l2-compliance tests passed successfully via:
$ v4l2-compliance -s -v -d /dev/video0
Pipelines were validated using the following GStreamer script.
[1] https://gist.github.com/moonlinux/08295ba1f17ce7155550773f2d2cb6e5
Previous changes:
v6: https://lore.kernel.org/all/20260530094326.11892-2-linux.amoon@gmail.com/
Thanks
-Annad
Anand Moon (19):
media: meson: vdec: Fix m2m device lifetime and cleanup path
media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and
session teardown
media: meson: vdec: Fix lifecycle leaks and race conditions in
recycle_thread
media: meson: vdec: Fix use-after-free race between teardown and ISR
routines
media: meson: vdec: Fix race condition and synchronize esparser IRQ
media: meson: vdec: Fix race condition by canceling work sync
media: meson: vdec: Refactor esparser work queue and fix teardown race
media: meson: vdec: Fix concurrent execution races and unsafe teardown
media: meson: vdec: Fix vp9 header update failure on invalid payloads
media: meson: vdec: Fix race conditions and leaks in esparser pipeline
media: meson: vdec: Update core m2m stream state during transitions
media: meson: vdec: Coordinate m2m task execution inside async loop
media: meson: vdec: Fix race conditions in job abort sequence
media: meson: vdec: Correct atomic counter placement in dst_buf_done
media: meson: vdec: Fix concurrent firmware loading race and hardware
timeout
media: meson: vdec: Configure DMA mask and segment size in probe
media: meson: canvas: Fix Use-After-Free by linking canvas provider
device
media: meson: vdec: Increase VIFIFO buffer size to 32 MiB
gpu: drm: meson: Fix DMA segment size limits and maximize allocation
boundaries
drivers/gpu/drm/meson/meson_drv.c | 6 +
drivers/soc/amlogic/meson-canvas.c | 14 +-
drivers/staging/media/meson/vdec/esparser.c | 127 +++++--
drivers/staging/media/meson/vdec/vdec.c | 320 ++++++++++++++----
drivers/staging/media/meson/vdec/vdec.h | 8 +-
drivers/staging/media/meson/vdec/vdec_1.c | 16 +-
.../staging/media/meson/vdec/vdec_helpers.c | 3 +
7 files changed, 388 insertions(+), 106 deletions(-)
base-commit: a13c140cc289c0b7b3770bce5b3ad42ab35074aa
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs
@ 2026-07-13 12:06 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk
This patch series addresses a collection of critical race conditions,
memory leaks, use-after-free bugs, and resource management issues
within the Amlogic Meson video decoder (meson-vdec) driver and
associated components.
The primary focus is tightening session lifecycles, safely synchronising
the V4L2 m2m framework callbacks, hardening work queue operations
(esparser_queue_work), and correcting buffer validations to prevent
stability issues like KASAN out-of-bounds errors or stream-on timeouts.
Summary of Changes:
* Race Conditions & Synchronisation: Fixes multi-threading and
execution races across stream-on, stream-off, job abort, and
teardown pathways.
* Memory Lifecycle: Eliminates memory leaks and use-after-free
vulnerabilities by safely pinning, canceling, and sequencing
device removals.
* VP9 & Buffer Hardening: Addresses payload handling bugs in the ES
parser, properly tracks ongoing buffers via atomic counters, and
prevents invalid memory writes.
* Platform/DMA Adjustments: Adjusts DMA segment configurations,
canvas handling, and buffer sizes to match modern hardware demands.
Note on an outstanding issue:
An execution deadlock occurs in the driver workqueue path during high
stress or long playback sessions. This stall is triggered when the
Amlogic hardware internal video FIFO buffer becomes completely full.
When esparser_queue_all_src runs, the capacity validation check is
tripped due to a design limitation in how buffer exhaustion is handled.
This blocks progress and leaves the workqueue worker waiting indefinitely
on a core mutex lock.
The resulting hung task call trace shows the worker thread blocking
on the internal lock:
Workqueue: events esparser_queue_all_src [meson_vdec]
...
__mutex_lock
mutex_lock_nested
esparser_queue_all_src
I have tried to address this issue, but the underlying deadlock persists
under extreme conditions.
Any feedback on this new appock is welcome.
Testing was done using v4l2-compliance and active decoding pipelines
on Meson platforms (like Hardkernel ODROID-N2+ and ODROID-C4) SBCs.
The v4l2-compliance tests passed successfully via:
$ v4l2-compliance -s -v -d /dev/video0
Pipelines were validated using the following GStreamer script.
[1] https://gist.github.com/moonlinux/08295ba1f17ce7155550773f2d2cb6e5
Previous changes:
v6: https://lore.kernel.org/all/20260530094326.11892-2-linux.amoon@gmail.com/
Thanks
-Annad
Anand Moon (19):
media: meson: vdec: Fix m2m device lifetime and cleanup path
media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and
session teardown
media: meson: vdec: Fix lifecycle leaks and race conditions in
recycle_thread
media: meson: vdec: Fix use-after-free race between teardown and ISR
routines
media: meson: vdec: Fix race condition and synchronize esparser IRQ
media: meson: vdec: Fix race condition by canceling work sync
media: meson: vdec: Refactor esparser work queue and fix teardown race
media: meson: vdec: Fix concurrent execution races and unsafe teardown
media: meson: vdec: Fix vp9 header update failure on invalid payloads
media: meson: vdec: Fix race conditions and leaks in esparser pipeline
media: meson: vdec: Update core m2m stream state during transitions
media: meson: vdec: Coordinate m2m task execution inside async loop
media: meson: vdec: Fix race conditions in job abort sequence
media: meson: vdec: Correct atomic counter placement in dst_buf_done
media: meson: vdec: Fix concurrent firmware loading race and hardware
timeout
media: meson: vdec: Configure DMA mask and segment size in probe
media: meson: canvas: Fix Use-After-Free by linking canvas provider
device
media: meson: vdec: Increase VIFIFO buffer size to 32 MiB
gpu: drm: meson: Fix DMA segment size limits and maximize allocation
boundaries
drivers/gpu/drm/meson/meson_drv.c | 6 +
drivers/soc/amlogic/meson-canvas.c | 14 +-
drivers/staging/media/meson/vdec/esparser.c | 127 +++++--
drivers/staging/media/meson/vdec/vdec.c | 320 ++++++++++++++----
drivers/staging/media/meson/vdec/vdec.h | 8 +-
drivers/staging/media/meson/vdec/vdec_1.c | 16 +-
.../staging/media/meson/vdec/vdec_helpers.c | 3 +
7 files changed, 388 insertions(+), 106 deletions(-)
base-commit: a13c140cc289c0b7b3770bce5b3ad42ab35074aa
--
2.50.1
^ permalink raw reply [flat|nested] 102+ messages in thread
* [PATCH v7 01/19] media: meson: vdec: Fix m2m device lifetime and cleanup path
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:06 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Sashiko
The vdec driver was incorrectly initializing a new v4l2_m2m device
instance per session inside vdec_open() and releasing it in vdec_close().
This design is faulty because the m2m device models the core hardware
engine and must persist across multiple sessions.
Fix the lifetime by moving v4l2_m2m_init() into vdec_probe() and
releasing it in vdec_remove() which resolves passing the global
core->m2m_dev down to session contexts so all sessions share the
same hardware instance.
This change aligns the driver with proper v4l2_m2m usage, ensuring
the hardware device lifetime is tied to the platform driver core,
not individual sessions, and making teardown safe and predictable.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260520045905.6ACBA1F000E9@smtp.kernel.org/#t
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 36 +++++++++++++------------
drivers/staging/media/meson/vdec/vdec.h | 4 +--
2 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index a039d925c0fe5..6ae3471155a87 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -153,7 +153,7 @@ static void vdec_m2m_job_abort(void *priv)
{
struct amvdec_session *sess = priv;
- v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
+ v4l2_m2m_job_finish(sess->core->m2m_dev, sess->m2m_ctx);
}
static const struct v4l2_m2m_ops vdec_m2m_ops = {
@@ -873,23 +873,16 @@ static int vdec_open(struct file *file)
sess->core = core;
- sess->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
- if (IS_ERR(sess->m2m_dev)) {
- dev_err(dev, "Fail to v4l2_m2m_init\n");
- ret = PTR_ERR(sess->m2m_dev);
- goto err_free_sess;
- }
-
- sess->m2m_ctx = v4l2_m2m_ctx_init(sess->m2m_dev, sess, m2m_queue_init);
+ sess->m2m_ctx = v4l2_m2m_ctx_init(core->m2m_dev, sess, m2m_queue_init);
if (IS_ERR(sess->m2m_ctx)) {
dev_err(dev, "Fail to v4l2_m2m_ctx_init\n");
ret = PTR_ERR(sess->m2m_ctx);
- goto err_m2m_release;
+ goto err_free_sess;
}
ret = vdec_init_ctrls(sess);
if (ret)
- goto err_m2m_ctx_release;
+ goto err_release_ctx;
sess->pixfmt_cap = formats[0].pixfmts_cap[0];
sess->fmt_out = &formats[0];
@@ -913,10 +906,8 @@ static int vdec_open(struct file *file)
return 0;
-err_m2m_ctx_release:
+err_release_ctx:
v4l2_m2m_ctx_release(sess->m2m_ctx);
-err_m2m_release:
- v4l2_m2m_release(sess->m2m_dev);
err_free_sess:
kfree(sess);
return ret;
@@ -927,9 +918,9 @@ static int vdec_close(struct file *file)
struct amvdec_session *sess = file_to_amvdec_session(file);
v4l2_m2m_ctx_release(sess->m2m_ctx);
- v4l2_m2m_release(sess->m2m_dev);
v4l2_fh_del(&sess->fh, file);
v4l2_fh_exit(&sess->fh);
+ v4l2_ctrl_handler_free(&sess->ctrl_handler);
mutex_destroy(&sess->lock);
mutex_destroy(&sess->bufs_recycle_lock);
@@ -1059,16 +1050,23 @@ static int vdec_probe(struct platform_device *pdev)
if (ret)
return ret;
+ core->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
+ if (IS_ERR(core->m2m_dev)) {
+ dev_err(dev, "Failed to initialize v4l2 m2m device\n");
+ return PTR_ERR(core->m2m_dev);
+ }
+
ret = v4l2_device_register(dev, &core->v4l2_dev);
if (ret) {
dev_err(dev, "Couldn't register v4l2 device\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_m2m_release;
}
vdev = video_device_alloc();
if (!vdev) {
ret = -ENOMEM;
- goto err_vdev_release;
+ goto err_v4l2_unregister;
}
core->vdev_dec = vdev;
@@ -1096,7 +1094,10 @@ static int vdec_probe(struct platform_device *pdev)
err_vdev_release:
video_device_release(vdev);
+err_v4l2_unregister:
v4l2_device_unregister(&core->v4l2_dev);
+err_m2m_release:
+ v4l2_m2m_release(core->m2m_dev);
return ret;
}
@@ -1105,6 +1106,7 @@ static void vdec_remove(struct platform_device *pdev)
struct amvdec_core *core = platform_get_drvdata(pdev);
video_unregister_device(core->vdev_dec);
+ v4l2_m2m_release(core->m2m_dev);
v4l2_device_unregister(&core->v4l2_dev);
}
diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
index 7a5d8e871d708..cc0cfafb8a951 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -63,6 +63,7 @@ struct amvdec_session;
* @vdec_hevcf_clk: VDEC_HEVCF clock
* @esparser_reset: RESET for the PARSER
* @vdev_dec: video device for the decoder
+ * @m2m_dev: v4l2 m2m device
* @v4l2_dev: v4l2 device
* @cur_sess: current decoding session
* @lock: video device lock
@@ -87,6 +88,7 @@ struct amvdec_core {
struct reset_control *esparser_reset;
struct video_device *vdev_dec;
+ struct v4l2_m2m_dev *m2m_dev;
struct v4l2_device v4l2_dev;
struct amvdec_session *cur_sess;
@@ -183,7 +185,6 @@ enum amvdec_status {
*
* @core: reference to the vdec core struct
* @fh: v4l2 file handle
- * @m2m_dev: v4l2 m2m device
* @m2m_ctx: v4l2 m2m context
* @ctrl_handler: V4L2 control handler
* @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE
@@ -230,7 +231,6 @@ struct amvdec_session {
struct amvdec_core *core;
struct v4l2_fh fh;
- struct v4l2_m2m_dev *m2m_dev;
struct v4l2_m2m_ctx *m2m_ctx;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_ctrl *ctrl_min_buf_capture;
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 01/19] media: meson: vdec: Fix m2m device lifetime and cleanup path
@ 2026-07-13 12:06 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko
The vdec driver was incorrectly initializing a new v4l2_m2m device
instance per session inside vdec_open() and releasing it in vdec_close().
This design is faulty because the m2m device models the core hardware
engine and must persist across multiple sessions.
Fix the lifetime by moving v4l2_m2m_init() into vdec_probe() and
releasing it in vdec_remove() which resolves passing the global
core->m2m_dev down to session contexts so all sessions share the
same hardware instance.
This change aligns the driver with proper v4l2_m2m usage, ensuring
the hardware device lifetime is tied to the platform driver core,
not individual sessions, and making teardown safe and predictable.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260520045905.6ACBA1F000E9@smtp.kernel.org/#t
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 36 +++++++++++++------------
drivers/staging/media/meson/vdec/vdec.h | 4 +--
2 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index a039d925c0fe5..6ae3471155a87 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -153,7 +153,7 @@ static void vdec_m2m_job_abort(void *priv)
{
struct amvdec_session *sess = priv;
- v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
+ v4l2_m2m_job_finish(sess->core->m2m_dev, sess->m2m_ctx);
}
static const struct v4l2_m2m_ops vdec_m2m_ops = {
@@ -873,23 +873,16 @@ static int vdec_open(struct file *file)
sess->core = core;
- sess->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
- if (IS_ERR(sess->m2m_dev)) {
- dev_err(dev, "Fail to v4l2_m2m_init\n");
- ret = PTR_ERR(sess->m2m_dev);
- goto err_free_sess;
- }
-
- sess->m2m_ctx = v4l2_m2m_ctx_init(sess->m2m_dev, sess, m2m_queue_init);
+ sess->m2m_ctx = v4l2_m2m_ctx_init(core->m2m_dev, sess, m2m_queue_init);
if (IS_ERR(sess->m2m_ctx)) {
dev_err(dev, "Fail to v4l2_m2m_ctx_init\n");
ret = PTR_ERR(sess->m2m_ctx);
- goto err_m2m_release;
+ goto err_free_sess;
}
ret = vdec_init_ctrls(sess);
if (ret)
- goto err_m2m_ctx_release;
+ goto err_release_ctx;
sess->pixfmt_cap = formats[0].pixfmts_cap[0];
sess->fmt_out = &formats[0];
@@ -913,10 +906,8 @@ static int vdec_open(struct file *file)
return 0;
-err_m2m_ctx_release:
+err_release_ctx:
v4l2_m2m_ctx_release(sess->m2m_ctx);
-err_m2m_release:
- v4l2_m2m_release(sess->m2m_dev);
err_free_sess:
kfree(sess);
return ret;
@@ -927,9 +918,9 @@ static int vdec_close(struct file *file)
struct amvdec_session *sess = file_to_amvdec_session(file);
v4l2_m2m_ctx_release(sess->m2m_ctx);
- v4l2_m2m_release(sess->m2m_dev);
v4l2_fh_del(&sess->fh, file);
v4l2_fh_exit(&sess->fh);
+ v4l2_ctrl_handler_free(&sess->ctrl_handler);
mutex_destroy(&sess->lock);
mutex_destroy(&sess->bufs_recycle_lock);
@@ -1059,16 +1050,23 @@ static int vdec_probe(struct platform_device *pdev)
if (ret)
return ret;
+ core->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
+ if (IS_ERR(core->m2m_dev)) {
+ dev_err(dev, "Failed to initialize v4l2 m2m device\n");
+ return PTR_ERR(core->m2m_dev);
+ }
+
ret = v4l2_device_register(dev, &core->v4l2_dev);
if (ret) {
dev_err(dev, "Couldn't register v4l2 device\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_m2m_release;
}
vdev = video_device_alloc();
if (!vdev) {
ret = -ENOMEM;
- goto err_vdev_release;
+ goto err_v4l2_unregister;
}
core->vdev_dec = vdev;
@@ -1096,7 +1094,10 @@ static int vdec_probe(struct platform_device *pdev)
err_vdev_release:
video_device_release(vdev);
+err_v4l2_unregister:
v4l2_device_unregister(&core->v4l2_dev);
+err_m2m_release:
+ v4l2_m2m_release(core->m2m_dev);
return ret;
}
@@ -1105,6 +1106,7 @@ static void vdec_remove(struct platform_device *pdev)
struct amvdec_core *core = platform_get_drvdata(pdev);
video_unregister_device(core->vdev_dec);
+ v4l2_m2m_release(core->m2m_dev);
v4l2_device_unregister(&core->v4l2_dev);
}
diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
index 7a5d8e871d708..cc0cfafb8a951 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -63,6 +63,7 @@ struct amvdec_session;
* @vdec_hevcf_clk: VDEC_HEVCF clock
* @esparser_reset: RESET for the PARSER
* @vdev_dec: video device for the decoder
+ * @m2m_dev: v4l2 m2m device
* @v4l2_dev: v4l2 device
* @cur_sess: current decoding session
* @lock: video device lock
@@ -87,6 +88,7 @@ struct amvdec_core {
struct reset_control *esparser_reset;
struct video_device *vdev_dec;
+ struct v4l2_m2m_dev *m2m_dev;
struct v4l2_device v4l2_dev;
struct amvdec_session *cur_sess;
@@ -183,7 +185,6 @@ enum amvdec_status {
*
* @core: reference to the vdec core struct
* @fh: v4l2 file handle
- * @m2m_dev: v4l2 m2m device
* @m2m_ctx: v4l2 m2m context
* @ctrl_handler: V4L2 control handler
* @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE
@@ -230,7 +231,6 @@ struct amvdec_session {
struct amvdec_core *core;
struct v4l2_fh fh;
- struct v4l2_m2m_dev *m2m_dev;
struct v4l2_m2m_ctx *m2m_ctx;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_ctrl *ctrl_min_buf_capture;
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 01/19] media: meson: vdec: Fix m2m device lifetime and cleanup path
@ 2026-07-13 12:06 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko
The vdec driver was incorrectly initializing a new v4l2_m2m device
instance per session inside vdec_open() and releasing it in vdec_close().
This design is faulty because the m2m device models the core hardware
engine and must persist across multiple sessions.
Fix the lifetime by moving v4l2_m2m_init() into vdec_probe() and
releasing it in vdec_remove() which resolves passing the global
core->m2m_dev down to session contexts so all sessions share the
same hardware instance.
This change aligns the driver with proper v4l2_m2m usage, ensuring
the hardware device lifetime is tied to the platform driver core,
not individual sessions, and making teardown safe and predictable.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260520045905.6ACBA1F000E9@smtp.kernel.org/#t
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 36 +++++++++++++------------
drivers/staging/media/meson/vdec/vdec.h | 4 +--
2 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index a039d925c0fe5..6ae3471155a87 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -153,7 +153,7 @@ static void vdec_m2m_job_abort(void *priv)
{
struct amvdec_session *sess = priv;
- v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
+ v4l2_m2m_job_finish(sess->core->m2m_dev, sess->m2m_ctx);
}
static const struct v4l2_m2m_ops vdec_m2m_ops = {
@@ -873,23 +873,16 @@ static int vdec_open(struct file *file)
sess->core = core;
- sess->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
- if (IS_ERR(sess->m2m_dev)) {
- dev_err(dev, "Fail to v4l2_m2m_init\n");
- ret = PTR_ERR(sess->m2m_dev);
- goto err_free_sess;
- }
-
- sess->m2m_ctx = v4l2_m2m_ctx_init(sess->m2m_dev, sess, m2m_queue_init);
+ sess->m2m_ctx = v4l2_m2m_ctx_init(core->m2m_dev, sess, m2m_queue_init);
if (IS_ERR(sess->m2m_ctx)) {
dev_err(dev, "Fail to v4l2_m2m_ctx_init\n");
ret = PTR_ERR(sess->m2m_ctx);
- goto err_m2m_release;
+ goto err_free_sess;
}
ret = vdec_init_ctrls(sess);
if (ret)
- goto err_m2m_ctx_release;
+ goto err_release_ctx;
sess->pixfmt_cap = formats[0].pixfmts_cap[0];
sess->fmt_out = &formats[0];
@@ -913,10 +906,8 @@ static int vdec_open(struct file *file)
return 0;
-err_m2m_ctx_release:
+err_release_ctx:
v4l2_m2m_ctx_release(sess->m2m_ctx);
-err_m2m_release:
- v4l2_m2m_release(sess->m2m_dev);
err_free_sess:
kfree(sess);
return ret;
@@ -927,9 +918,9 @@ static int vdec_close(struct file *file)
struct amvdec_session *sess = file_to_amvdec_session(file);
v4l2_m2m_ctx_release(sess->m2m_ctx);
- v4l2_m2m_release(sess->m2m_dev);
v4l2_fh_del(&sess->fh, file);
v4l2_fh_exit(&sess->fh);
+ v4l2_ctrl_handler_free(&sess->ctrl_handler);
mutex_destroy(&sess->lock);
mutex_destroy(&sess->bufs_recycle_lock);
@@ -1059,16 +1050,23 @@ static int vdec_probe(struct platform_device *pdev)
if (ret)
return ret;
+ core->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
+ if (IS_ERR(core->m2m_dev)) {
+ dev_err(dev, "Failed to initialize v4l2 m2m device\n");
+ return PTR_ERR(core->m2m_dev);
+ }
+
ret = v4l2_device_register(dev, &core->v4l2_dev);
if (ret) {
dev_err(dev, "Couldn't register v4l2 device\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_m2m_release;
}
vdev = video_device_alloc();
if (!vdev) {
ret = -ENOMEM;
- goto err_vdev_release;
+ goto err_v4l2_unregister;
}
core->vdev_dec = vdev;
@@ -1096,7 +1094,10 @@ static int vdec_probe(struct platform_device *pdev)
err_vdev_release:
video_device_release(vdev);
+err_v4l2_unregister:
v4l2_device_unregister(&core->v4l2_dev);
+err_m2m_release:
+ v4l2_m2m_release(core->m2m_dev);
return ret;
}
@@ -1105,6 +1106,7 @@ static void vdec_remove(struct platform_device *pdev)
struct amvdec_core *core = platform_get_drvdata(pdev);
video_unregister_device(core->vdev_dec);
+ v4l2_m2m_release(core->m2m_dev);
v4l2_device_unregister(&core->v4l2_dev);
}
diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
index 7a5d8e871d708..cc0cfafb8a951 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -63,6 +63,7 @@ struct amvdec_session;
* @vdec_hevcf_clk: VDEC_HEVCF clock
* @esparser_reset: RESET for the PARSER
* @vdev_dec: video device for the decoder
+ * @m2m_dev: v4l2 m2m device
* @v4l2_dev: v4l2 device
* @cur_sess: current decoding session
* @lock: video device lock
@@ -87,6 +88,7 @@ struct amvdec_core {
struct reset_control *esparser_reset;
struct video_device *vdev_dec;
+ struct v4l2_m2m_dev *m2m_dev;
struct v4l2_device v4l2_dev;
struct amvdec_session *cur_sess;
@@ -183,7 +185,6 @@ enum amvdec_status {
*
* @core: reference to the vdec core struct
* @fh: v4l2 file handle
- * @m2m_dev: v4l2 m2m device
* @m2m_ctx: v4l2 m2m context
* @ctrl_handler: V4L2 control handler
* @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE
@@ -230,7 +231,6 @@ struct amvdec_session {
struct amvdec_core *core;
struct v4l2_fh fh;
- struct v4l2_m2m_dev *m2m_dev;
struct v4l2_m2m_ctx *m2m_ctx;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_ctrl *ctrl_min_buf_capture;
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 02/19] media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:06 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne, Sashiko
The vdec driver suffered from unsafe state transitions and race
conditions when handling concurrent STREAMON / STREAMOFF calls and
dynamic resolution change (DRC) events. Global context pointers
(e.g. core->cur_sess) and session status flags were updated outside
proper lock boundaries, allowing parallel threads to corrupt hardware
state under load.
Address these architectural stability flaws with the following changes:
1. In vdec_start_streaming(), safely encapsulate hardware occupancy
evaluations and target session context claims within a secure mutex
lock (core->lock). This blocks overlapping multi-threaded STREAMON
calls from creating concurrent assignment conflicts.
2. Restructure initialization error path handlers to avoid global memory
maps cross-contamination. Segregate buffer flushes cleanly based
strictly on the active vb2 queue type (OUTPUT vs CAPTURE) to avoid
incorrectly reclaiming undecoded ready blocks.
3. In vdec_stop_streaming(), introduce state-aware tracking to
accurately identify DRC conditions. If a resolution modification
forces a capture queue reset while the output stream is still live,
preserve the driver's inner runtime states and bypass premature
hardware power-off sweeps.
4. Enforce strict null-pointer safety limits inside DMA unmapping
sequences. Explicitly clear tracking metadata entries
(sess->vififo_vaddr = NULL) inside the VIFIFO freeing routines to
neutralize accidental double-free risks.
Together these changes harden the driver against concurrency bugs,
eliminate memory leaks, and ensure predictable session lifecycle
management under multi threaded workloads.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260530100841.9CEBA1F00893@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 136 +++++++++++++++++-------
1 file changed, 95 insertions(+), 41 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 6ae3471155a87..d1f35fc893de1 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -286,11 +286,6 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
struct vb2_v4l2_buffer *buf;
int ret;
- if (core->cur_sess && core->cur_sess != sess) {
- ret = -EBUSY;
- goto bufs_done;
- }
-
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
sess->streamon_out = 1;
else
@@ -308,9 +303,29 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
}
if (sess->status == STATUS_RUNNING ||
- sess->status == STATUS_NEEDS_RESUME ||
- sess->status == STATUS_INIT)
+ sess->status == STATUS_NEEDS_RESUME)
+ return 0;
+
+ /*
+ * Secure the core hardware lock before checking availability
+ * and updating session states to prevent STREAMON race conditions.
+ */
+ mutex_lock(&core->lock);
+ if (core->cur_sess && core->cur_sess != sess) {
+ ret = -EBUSY;
+ mutex_unlock(&core->lock);
+ goto err_unlock_no_hw;
+ }
+
+ /* If already half-initialized, do not re-initialize */
+ if (sess->status == STATUS_INIT) {
+ mutex_unlock(&core->lock);
return 0;
+ }
+
+ sess->status = STATUS_INIT;
+ core->cur_sess = sess;
+ mutex_unlock(&core->lock);
sess->vififo_size = SIZE_VIFIFO;
sess->vififo_vaddr =
@@ -319,7 +334,7 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
if (!sess->vififo_vaddr) {
dev_err(sess->core->dev, "Failed to request VIFIFO buffer\n");
ret = -ENOMEM;
- goto bufs_done;
+ goto err_cleanup_session;
}
sess->should_stop = 0;
@@ -333,33 +348,43 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
ret = vdec_poweron(sess);
if (ret)
- goto vififo_free;
+ goto err_free_vififo;
sess->sequence_cap = 0;
sess->sequence_out = 0;
+
if (vdec_codec_needs_recycle(sess))
sess->recycle_thread = kthread_run(vdec_recycle_thread, sess,
"vdec_recycle");
- sess->status = STATUS_INIT;
- core->cur_sess = sess;
schedule_work(&sess->esparser_queue_work);
return 0;
-vififo_free:
- dma_free_coherent(sess->core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
-bufs_done:
- while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
- v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
- while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
- v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
-
+err_free_vififo:
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(sess->core->dev, sess->vififo_size,
+ sess->vififo_vaddr, sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+err_cleanup_session:
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
sess->streamon_out = 0;
else
sess->streamon_cap = 0;
+ mutex_lock(&core->lock);
+ if (core->cur_sess == sess)
+ core->cur_sess = NULL;
+ if (sess->status != STATUS_NEEDS_RESUME)
+ sess->status = STATUS_STOPPED;
+ mutex_unlock(&core->lock);
+err_unlock_no_hw:
+ while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
+ while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
+
return ret;
}
@@ -399,30 +424,13 @@ static void vdec_stop_streaming(struct vb2_queue *q)
struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
struct amvdec_core *core = sess->core;
struct vb2_v4l2_buffer *buf;
+ enum amvdec_status old_status;
+ bool full_cleanup = false;
- if (sess->status == STATUS_RUNNING ||
- sess->status == STATUS_INIT ||
- (sess->status == STATUS_NEEDS_RESUME &&
- (!sess->streamon_out || !sess->streamon_cap))) {
- if (vdec_codec_needs_recycle(sess))
- kthread_stop(sess->recycle_thread);
-
- vdec_poweroff(sess);
- vdec_free_canvas(sess);
- dma_free_coherent(sess->core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
- vdec_reset_timestamps(sess);
- vdec_reset_bufs_recycle(sess);
- kfree(sess->priv);
- sess->priv = NULL;
- core->cur_sess = NULL;
- sess->status = STATUS_STOPPED;
- }
-
+ /* flush buffers to kill background workqueue thread */
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
-
sess->streamon_out = 0;
} else {
/* Drain remaining refs if was still running */
@@ -431,9 +439,55 @@ static void vdec_stop_streaming(struct vb2_queue *q)
while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
-
sess->streamon_cap = 0;
}
+
+ /* Hold core lock continuously for the state and resource processing */
+ mutex_lock(&core->lock);
+ old_status = sess->status;
+
+ if (old_status == STATUS_RUNNING || old_status == STATUS_INIT ||
+ (old_status == STATUS_NEEDS_RESUME && (!sess->streamon_out ||
+ !sess->streamon_cap))) {
+ /*
+ * If it's a DRC event (Capture queue streamoff only), preserve
+ * the status
+ */
+ if (old_status == STATUS_NEEDS_RESUME && sess->streamon_out) {
+ full_cleanup = false;
+ } else {
+ full_cleanup = true;
+ sess->status = STATUS_STOPPED;
+ }
+ }
+
+ if (full_cleanup) {
+ if ((q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ !sess->streamon_out) && vdec_codec_needs_recycle(sess)) {
+ kthread_stop(sess->recycle_thread);
+ }
+
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(sess->core->dev, sess->vififo_size,
+ sess->vififo_vaddr, sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
+ core->cur_sess = NULL;
+
+ kfree(sess->priv);
+ sess->priv = NULL;
+ } else {
+ if (sess->status == STATUS_NEEDS_RESUME)
+ sess->changed_format = 0;
+ }
+ mutex_unlock(&core->lock);
}
static int vdec_vb2_buf_prepare(struct vb2_buffer *vb)
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 02/19] media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown
@ 2026-07-13 12:06 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
The vdec driver suffered from unsafe state transitions and race
conditions when handling concurrent STREAMON / STREAMOFF calls and
dynamic resolution change (DRC) events. Global context pointers
(e.g. core->cur_sess) and session status flags were updated outside
proper lock boundaries, allowing parallel threads to corrupt hardware
state under load.
Address these architectural stability flaws with the following changes:
1. In vdec_start_streaming(), safely encapsulate hardware occupancy
evaluations and target session context claims within a secure mutex
lock (core->lock). This blocks overlapping multi-threaded STREAMON
calls from creating concurrent assignment conflicts.
2. Restructure initialization error path handlers to avoid global memory
maps cross-contamination. Segregate buffer flushes cleanly based
strictly on the active vb2 queue type (OUTPUT vs CAPTURE) to avoid
incorrectly reclaiming undecoded ready blocks.
3. In vdec_stop_streaming(), introduce state-aware tracking to
accurately identify DRC conditions. If a resolution modification
forces a capture queue reset while the output stream is still live,
preserve the driver's inner runtime states and bypass premature
hardware power-off sweeps.
4. Enforce strict null-pointer safety limits inside DMA unmapping
sequences. Explicitly clear tracking metadata entries
(sess->vififo_vaddr = NULL) inside the VIFIFO freeing routines to
neutralize accidental double-free risks.
Together these changes harden the driver against concurrency bugs,
eliminate memory leaks, and ensure predictable session lifecycle
management under multi threaded workloads.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260530100841.9CEBA1F00893@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 136 +++++++++++++++++-------
1 file changed, 95 insertions(+), 41 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 6ae3471155a87..d1f35fc893de1 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -286,11 +286,6 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
struct vb2_v4l2_buffer *buf;
int ret;
- if (core->cur_sess && core->cur_sess != sess) {
- ret = -EBUSY;
- goto bufs_done;
- }
-
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
sess->streamon_out = 1;
else
@@ -308,9 +303,29 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
}
if (sess->status == STATUS_RUNNING ||
- sess->status == STATUS_NEEDS_RESUME ||
- sess->status == STATUS_INIT)
+ sess->status == STATUS_NEEDS_RESUME)
+ return 0;
+
+ /*
+ * Secure the core hardware lock before checking availability
+ * and updating session states to prevent STREAMON race conditions.
+ */
+ mutex_lock(&core->lock);
+ if (core->cur_sess && core->cur_sess != sess) {
+ ret = -EBUSY;
+ mutex_unlock(&core->lock);
+ goto err_unlock_no_hw;
+ }
+
+ /* If already half-initialized, do not re-initialize */
+ if (sess->status == STATUS_INIT) {
+ mutex_unlock(&core->lock);
return 0;
+ }
+
+ sess->status = STATUS_INIT;
+ core->cur_sess = sess;
+ mutex_unlock(&core->lock);
sess->vififo_size = SIZE_VIFIFO;
sess->vififo_vaddr =
@@ -319,7 +334,7 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
if (!sess->vififo_vaddr) {
dev_err(sess->core->dev, "Failed to request VIFIFO buffer\n");
ret = -ENOMEM;
- goto bufs_done;
+ goto err_cleanup_session;
}
sess->should_stop = 0;
@@ -333,33 +348,43 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
ret = vdec_poweron(sess);
if (ret)
- goto vififo_free;
+ goto err_free_vififo;
sess->sequence_cap = 0;
sess->sequence_out = 0;
+
if (vdec_codec_needs_recycle(sess))
sess->recycle_thread = kthread_run(vdec_recycle_thread, sess,
"vdec_recycle");
- sess->status = STATUS_INIT;
- core->cur_sess = sess;
schedule_work(&sess->esparser_queue_work);
return 0;
-vififo_free:
- dma_free_coherent(sess->core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
-bufs_done:
- while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
- v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
- while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
- v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
-
+err_free_vififo:
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(sess->core->dev, sess->vififo_size,
+ sess->vififo_vaddr, sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+err_cleanup_session:
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
sess->streamon_out = 0;
else
sess->streamon_cap = 0;
+ mutex_lock(&core->lock);
+ if (core->cur_sess == sess)
+ core->cur_sess = NULL;
+ if (sess->status != STATUS_NEEDS_RESUME)
+ sess->status = STATUS_STOPPED;
+ mutex_unlock(&core->lock);
+err_unlock_no_hw:
+ while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
+ while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
+
return ret;
}
@@ -399,30 +424,13 @@ static void vdec_stop_streaming(struct vb2_queue *q)
struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
struct amvdec_core *core = sess->core;
struct vb2_v4l2_buffer *buf;
+ enum amvdec_status old_status;
+ bool full_cleanup = false;
- if (sess->status == STATUS_RUNNING ||
- sess->status == STATUS_INIT ||
- (sess->status == STATUS_NEEDS_RESUME &&
- (!sess->streamon_out || !sess->streamon_cap))) {
- if (vdec_codec_needs_recycle(sess))
- kthread_stop(sess->recycle_thread);
-
- vdec_poweroff(sess);
- vdec_free_canvas(sess);
- dma_free_coherent(sess->core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
- vdec_reset_timestamps(sess);
- vdec_reset_bufs_recycle(sess);
- kfree(sess->priv);
- sess->priv = NULL;
- core->cur_sess = NULL;
- sess->status = STATUS_STOPPED;
- }
-
+ /* flush buffers to kill background workqueue thread */
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
-
sess->streamon_out = 0;
} else {
/* Drain remaining refs if was still running */
@@ -431,9 +439,55 @@ static void vdec_stop_streaming(struct vb2_queue *q)
while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
-
sess->streamon_cap = 0;
}
+
+ /* Hold core lock continuously for the state and resource processing */
+ mutex_lock(&core->lock);
+ old_status = sess->status;
+
+ if (old_status == STATUS_RUNNING || old_status == STATUS_INIT ||
+ (old_status == STATUS_NEEDS_RESUME && (!sess->streamon_out ||
+ !sess->streamon_cap))) {
+ /*
+ * If it's a DRC event (Capture queue streamoff only), preserve
+ * the status
+ */
+ if (old_status == STATUS_NEEDS_RESUME && sess->streamon_out) {
+ full_cleanup = false;
+ } else {
+ full_cleanup = true;
+ sess->status = STATUS_STOPPED;
+ }
+ }
+
+ if (full_cleanup) {
+ if ((q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ !sess->streamon_out) && vdec_codec_needs_recycle(sess)) {
+ kthread_stop(sess->recycle_thread);
+ }
+
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(sess->core->dev, sess->vififo_size,
+ sess->vififo_vaddr, sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
+ core->cur_sess = NULL;
+
+ kfree(sess->priv);
+ sess->priv = NULL;
+ } else {
+ if (sess->status == STATUS_NEEDS_RESUME)
+ sess->changed_format = 0;
+ }
+ mutex_unlock(&core->lock);
}
static int vdec_vb2_buf_prepare(struct vb2_buffer *vb)
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 02/19] media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown
@ 2026-07-13 12:06 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
The vdec driver suffered from unsafe state transitions and race
conditions when handling concurrent STREAMON / STREAMOFF calls and
dynamic resolution change (DRC) events. Global context pointers
(e.g. core->cur_sess) and session status flags were updated outside
proper lock boundaries, allowing parallel threads to corrupt hardware
state under load.
Address these architectural stability flaws with the following changes:
1. In vdec_start_streaming(), safely encapsulate hardware occupancy
evaluations and target session context claims within a secure mutex
lock (core->lock). This blocks overlapping multi-threaded STREAMON
calls from creating concurrent assignment conflicts.
2. Restructure initialization error path handlers to avoid global memory
maps cross-contamination. Segregate buffer flushes cleanly based
strictly on the active vb2 queue type (OUTPUT vs CAPTURE) to avoid
incorrectly reclaiming undecoded ready blocks.
3. In vdec_stop_streaming(), introduce state-aware tracking to
accurately identify DRC conditions. If a resolution modification
forces a capture queue reset while the output stream is still live,
preserve the driver's inner runtime states and bypass premature
hardware power-off sweeps.
4. Enforce strict null-pointer safety limits inside DMA unmapping
sequences. Explicitly clear tracking metadata entries
(sess->vififo_vaddr = NULL) inside the VIFIFO freeing routines to
neutralize accidental double-free risks.
Together these changes harden the driver against concurrency bugs,
eliminate memory leaks, and ensure predictable session lifecycle
management under multi threaded workloads.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260530100841.9CEBA1F00893@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 136 +++++++++++++++++-------
1 file changed, 95 insertions(+), 41 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 6ae3471155a87..d1f35fc893de1 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -286,11 +286,6 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
struct vb2_v4l2_buffer *buf;
int ret;
- if (core->cur_sess && core->cur_sess != sess) {
- ret = -EBUSY;
- goto bufs_done;
- }
-
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
sess->streamon_out = 1;
else
@@ -308,9 +303,29 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
}
if (sess->status == STATUS_RUNNING ||
- sess->status == STATUS_NEEDS_RESUME ||
- sess->status == STATUS_INIT)
+ sess->status == STATUS_NEEDS_RESUME)
+ return 0;
+
+ /*
+ * Secure the core hardware lock before checking availability
+ * and updating session states to prevent STREAMON race conditions.
+ */
+ mutex_lock(&core->lock);
+ if (core->cur_sess && core->cur_sess != sess) {
+ ret = -EBUSY;
+ mutex_unlock(&core->lock);
+ goto err_unlock_no_hw;
+ }
+
+ /* If already half-initialized, do not re-initialize */
+ if (sess->status == STATUS_INIT) {
+ mutex_unlock(&core->lock);
return 0;
+ }
+
+ sess->status = STATUS_INIT;
+ core->cur_sess = sess;
+ mutex_unlock(&core->lock);
sess->vififo_size = SIZE_VIFIFO;
sess->vififo_vaddr =
@@ -319,7 +334,7 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
if (!sess->vififo_vaddr) {
dev_err(sess->core->dev, "Failed to request VIFIFO buffer\n");
ret = -ENOMEM;
- goto bufs_done;
+ goto err_cleanup_session;
}
sess->should_stop = 0;
@@ -333,33 +348,43 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
ret = vdec_poweron(sess);
if (ret)
- goto vififo_free;
+ goto err_free_vififo;
sess->sequence_cap = 0;
sess->sequence_out = 0;
+
if (vdec_codec_needs_recycle(sess))
sess->recycle_thread = kthread_run(vdec_recycle_thread, sess,
"vdec_recycle");
- sess->status = STATUS_INIT;
- core->cur_sess = sess;
schedule_work(&sess->esparser_queue_work);
return 0;
-vififo_free:
- dma_free_coherent(sess->core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
-bufs_done:
- while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
- v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
- while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
- v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
-
+err_free_vififo:
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(sess->core->dev, sess->vififo_size,
+ sess->vififo_vaddr, sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+err_cleanup_session:
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
sess->streamon_out = 0;
else
sess->streamon_cap = 0;
+ mutex_lock(&core->lock);
+ if (core->cur_sess == sess)
+ core->cur_sess = NULL;
+ if (sess->status != STATUS_NEEDS_RESUME)
+ sess->status = STATUS_STOPPED;
+ mutex_unlock(&core->lock);
+err_unlock_no_hw:
+ while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
+ while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
+
return ret;
}
@@ -399,30 +424,13 @@ static void vdec_stop_streaming(struct vb2_queue *q)
struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
struct amvdec_core *core = sess->core;
struct vb2_v4l2_buffer *buf;
+ enum amvdec_status old_status;
+ bool full_cleanup = false;
- if (sess->status == STATUS_RUNNING ||
- sess->status == STATUS_INIT ||
- (sess->status == STATUS_NEEDS_RESUME &&
- (!sess->streamon_out || !sess->streamon_cap))) {
- if (vdec_codec_needs_recycle(sess))
- kthread_stop(sess->recycle_thread);
-
- vdec_poweroff(sess);
- vdec_free_canvas(sess);
- dma_free_coherent(sess->core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
- vdec_reset_timestamps(sess);
- vdec_reset_bufs_recycle(sess);
- kfree(sess->priv);
- sess->priv = NULL;
- core->cur_sess = NULL;
- sess->status = STATUS_STOPPED;
- }
-
+ /* flush buffers to kill background workqueue thread */
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
-
sess->streamon_out = 0;
} else {
/* Drain remaining refs if was still running */
@@ -431,9 +439,55 @@ static void vdec_stop_streaming(struct vb2_queue *q)
while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
-
sess->streamon_cap = 0;
}
+
+ /* Hold core lock continuously for the state and resource processing */
+ mutex_lock(&core->lock);
+ old_status = sess->status;
+
+ if (old_status == STATUS_RUNNING || old_status == STATUS_INIT ||
+ (old_status == STATUS_NEEDS_RESUME && (!sess->streamon_out ||
+ !sess->streamon_cap))) {
+ /*
+ * If it's a DRC event (Capture queue streamoff only), preserve
+ * the status
+ */
+ if (old_status == STATUS_NEEDS_RESUME && sess->streamon_out) {
+ full_cleanup = false;
+ } else {
+ full_cleanup = true;
+ sess->status = STATUS_STOPPED;
+ }
+ }
+
+ if (full_cleanup) {
+ if ((q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ !sess->streamon_out) && vdec_codec_needs_recycle(sess)) {
+ kthread_stop(sess->recycle_thread);
+ }
+
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(sess->core->dev, sess->vififo_size,
+ sess->vififo_vaddr, sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
+ core->cur_sess = NULL;
+
+ kfree(sess->priv);
+ sess->priv = NULL;
+ } else {
+ if (sess->status == STATUS_NEEDS_RESUME)
+ sess->changed_format = 0;
+ }
+ mutex_unlock(&core->lock);
}
static int vdec_vb2_buf_prepare(struct vb2_buffer *vb)
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 03/19] media: meson: vdec: Fix lifecycle leaks and race conditions in recycle_thread
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:06 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne, Sashiko
Validate the return status of kthread_run() during decoder recycling
thread initialization to prevent memory leaks, and enforce robust state
checking before invoking kthread_stop() to prevent kernel panics.
If the system operates under severe memory constraints, the previous
implementation silently accepted error pointers returned by a failed
kthread_run() operation. Attempting to pass these invalid error pointers
downstream to kthread_stop() during streaming stop or application
teardown routines triggered instant kernel panics.
Furthermore, when thread initialization failed, the lack of an explicit
unwinding path leaked the session's private context (sess->priv) and its
associated firmware memory block allocations. Because sess->status was
never transitioned to STATUS_INIT, any subsequent call to stop streaming
or close the driver file node failed its state validation checks,
permanently orphaning active platform DMA assets and codec parameters.
Fix these thread lifetime and memory leak bugs via the following:
1. In vdec_start_streaming(), add an explicit IS_ERR() verification
barrier immediately following the recycling kthread launch. If thread
creation fails, capture the error code via PTR_ERR(), clear the
dangling pointer tracking entry to NULL, and jump to a newly added
'err_poweroff' label to unwind hardware settings and clear out
allocated structural components cleanly.
2. In vdec_stop_streaming(), protect the teardown execution track by
wrapping the kthread_stop() invocation inside a safe !IS_ERR_OR_NULL()
conditional block to ensure the target thread structure actually exists
in system memory before stopping it.
3. In vdec_close(), cleanly wind down and terminate any lingering
recycling threads at the absolute entry boundary of the routine
before flushing data framework contexts and releasing the file
descriptor matrices to user space.
Together, these changes ensure predictable driver behavior, prevent
kernel panics during low-memory conditions, and guarantee that memory
and DMA assets are fully reclaimed during teardown.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 48 +++++++++++++++++++++++--
1 file changed, 45 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index d1f35fc893de1..7fc73d5cdebbf 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -32,6 +32,10 @@ struct dummy_buf {
/* 16 MiB for parsed bitstream swap exchange */
#define SIZE_VIFIFO SZ_16M
+static void vdec_free_canvas(struct amvdec_session *sess);
+static void vdec_reset_timestamps(struct amvdec_session *sess);
+static void vdec_reset_bufs_recycle(struct amvdec_session *sess);
+
static u32 get_output_size(u32 width, u32 height)
{
return ALIGN(width * height, SZ_64K);
@@ -353,13 +357,23 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
sess->sequence_cap = 0;
sess->sequence_out = 0;
- if (vdec_codec_needs_recycle(sess))
+ if (vdec_codec_needs_recycle(sess) && !sess->recycle_thread) {
sess->recycle_thread = kthread_run(vdec_recycle_thread, sess,
"vdec_recycle");
-
+ if (IS_ERR(sess->recycle_thread)) {
+ ret = PTR_ERR(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ goto err_poweroff;
+ }
+ }
schedule_work(&sess->esparser_queue_work);
return 0;
+err_poweroff:
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
err_free_vififo:
if (sess->vififo_vaddr) {
dma_free_coherent(sess->core->dev, sess->vififo_size,
@@ -464,7 +478,10 @@ static void vdec_stop_streaming(struct vb2_queue *q)
if (full_cleanup) {
if ((q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
!sess->streamon_out) && vdec_codec_needs_recycle(sess)) {
- kthread_stop(sess->recycle_thread);
+ if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
+ kthread_stop(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ }
}
vdec_poweroff(sess);
@@ -970,6 +987,31 @@ static int vdec_open(struct file *file)
static int vdec_close(struct file *file)
{
struct amvdec_session *sess = file_to_amvdec_session(file);
+ struct amvdec_core *core = sess->core;
+
+ if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
+ kthread_stop(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ }
+
+ mutex_lock(&core->lock);
+
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+ core->cur_sess = NULL;
+
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(core->dev, sess->vififo_size,
+ sess->vififo_vaddr, sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
+ kfree(sess->priv);
+ sess->priv = NULL;
+
+ mutex_unlock(&core->lock);
v4l2_m2m_ctx_release(sess->m2m_ctx);
v4l2_fh_del(&sess->fh, file);
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 03/19] media: meson: vdec: Fix lifecycle leaks and race conditions in recycle_thread
@ 2026-07-13 12:06 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
Validate the return status of kthread_run() during decoder recycling
thread initialization to prevent memory leaks, and enforce robust state
checking before invoking kthread_stop() to prevent kernel panics.
If the system operates under severe memory constraints, the previous
implementation silently accepted error pointers returned by a failed
kthread_run() operation. Attempting to pass these invalid error pointers
downstream to kthread_stop() during streaming stop or application
teardown routines triggered instant kernel panics.
Furthermore, when thread initialization failed, the lack of an explicit
unwinding path leaked the session's private context (sess->priv) and its
associated firmware memory block allocations. Because sess->status was
never transitioned to STATUS_INIT, any subsequent call to stop streaming
or close the driver file node failed its state validation checks,
permanently orphaning active platform DMA assets and codec parameters.
Fix these thread lifetime and memory leak bugs via the following:
1. In vdec_start_streaming(), add an explicit IS_ERR() verification
barrier immediately following the recycling kthread launch. If thread
creation fails, capture the error code via PTR_ERR(), clear the
dangling pointer tracking entry to NULL, and jump to a newly added
'err_poweroff' label to unwind hardware settings and clear out
allocated structural components cleanly.
2. In vdec_stop_streaming(), protect the teardown execution track by
wrapping the kthread_stop() invocation inside a safe !IS_ERR_OR_NULL()
conditional block to ensure the target thread structure actually exists
in system memory before stopping it.
3. In vdec_close(), cleanly wind down and terminate any lingering
recycling threads at the absolute entry boundary of the routine
before flushing data framework contexts and releasing the file
descriptor matrices to user space.
Together, these changes ensure predictable driver behavior, prevent
kernel panics during low-memory conditions, and guarantee that memory
and DMA assets are fully reclaimed during teardown.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 48 +++++++++++++++++++++++--
1 file changed, 45 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index d1f35fc893de1..7fc73d5cdebbf 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -32,6 +32,10 @@ struct dummy_buf {
/* 16 MiB for parsed bitstream swap exchange */
#define SIZE_VIFIFO SZ_16M
+static void vdec_free_canvas(struct amvdec_session *sess);
+static void vdec_reset_timestamps(struct amvdec_session *sess);
+static void vdec_reset_bufs_recycle(struct amvdec_session *sess);
+
static u32 get_output_size(u32 width, u32 height)
{
return ALIGN(width * height, SZ_64K);
@@ -353,13 +357,23 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
sess->sequence_cap = 0;
sess->sequence_out = 0;
- if (vdec_codec_needs_recycle(sess))
+ if (vdec_codec_needs_recycle(sess) && !sess->recycle_thread) {
sess->recycle_thread = kthread_run(vdec_recycle_thread, sess,
"vdec_recycle");
-
+ if (IS_ERR(sess->recycle_thread)) {
+ ret = PTR_ERR(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ goto err_poweroff;
+ }
+ }
schedule_work(&sess->esparser_queue_work);
return 0;
+err_poweroff:
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
err_free_vififo:
if (sess->vififo_vaddr) {
dma_free_coherent(sess->core->dev, sess->vififo_size,
@@ -464,7 +478,10 @@ static void vdec_stop_streaming(struct vb2_queue *q)
if (full_cleanup) {
if ((q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
!sess->streamon_out) && vdec_codec_needs_recycle(sess)) {
- kthread_stop(sess->recycle_thread);
+ if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
+ kthread_stop(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ }
}
vdec_poweroff(sess);
@@ -970,6 +987,31 @@ static int vdec_open(struct file *file)
static int vdec_close(struct file *file)
{
struct amvdec_session *sess = file_to_amvdec_session(file);
+ struct amvdec_core *core = sess->core;
+
+ if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
+ kthread_stop(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ }
+
+ mutex_lock(&core->lock);
+
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+ core->cur_sess = NULL;
+
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(core->dev, sess->vififo_size,
+ sess->vififo_vaddr, sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
+ kfree(sess->priv);
+ sess->priv = NULL;
+
+ mutex_unlock(&core->lock);
v4l2_m2m_ctx_release(sess->m2m_ctx);
v4l2_fh_del(&sess->fh, file);
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 03/19] media: meson: vdec: Fix lifecycle leaks and race conditions in recycle_thread
@ 2026-07-13 12:06 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
Validate the return status of kthread_run() during decoder recycling
thread initialization to prevent memory leaks, and enforce robust state
checking before invoking kthread_stop() to prevent kernel panics.
If the system operates under severe memory constraints, the previous
implementation silently accepted error pointers returned by a failed
kthread_run() operation. Attempting to pass these invalid error pointers
downstream to kthread_stop() during streaming stop or application
teardown routines triggered instant kernel panics.
Furthermore, when thread initialization failed, the lack of an explicit
unwinding path leaked the session's private context (sess->priv) and its
associated firmware memory block allocations. Because sess->status was
never transitioned to STATUS_INIT, any subsequent call to stop streaming
or close the driver file node failed its state validation checks,
permanently orphaning active platform DMA assets and codec parameters.
Fix these thread lifetime and memory leak bugs via the following:
1. In vdec_start_streaming(), add an explicit IS_ERR() verification
barrier immediately following the recycling kthread launch. If thread
creation fails, capture the error code via PTR_ERR(), clear the
dangling pointer tracking entry to NULL, and jump to a newly added
'err_poweroff' label to unwind hardware settings and clear out
allocated structural components cleanly.
2. In vdec_stop_streaming(), protect the teardown execution track by
wrapping the kthread_stop() invocation inside a safe !IS_ERR_OR_NULL()
conditional block to ensure the target thread structure actually exists
in system memory before stopping it.
3. In vdec_close(), cleanly wind down and terminate any lingering
recycling threads at the absolute entry boundary of the routine
before flushing data framework contexts and releasing the file
descriptor matrices to user space.
Together, these changes ensure predictable driver behavior, prevent
kernel panics during low-memory conditions, and guarantee that memory
and DMA assets are fully reclaimed during teardown.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 48 +++++++++++++++++++++++--
1 file changed, 45 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index d1f35fc893de1..7fc73d5cdebbf 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -32,6 +32,10 @@ struct dummy_buf {
/* 16 MiB for parsed bitstream swap exchange */
#define SIZE_VIFIFO SZ_16M
+static void vdec_free_canvas(struct amvdec_session *sess);
+static void vdec_reset_timestamps(struct amvdec_session *sess);
+static void vdec_reset_bufs_recycle(struct amvdec_session *sess);
+
static u32 get_output_size(u32 width, u32 height)
{
return ALIGN(width * height, SZ_64K);
@@ -353,13 +357,23 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
sess->sequence_cap = 0;
sess->sequence_out = 0;
- if (vdec_codec_needs_recycle(sess))
+ if (vdec_codec_needs_recycle(sess) && !sess->recycle_thread) {
sess->recycle_thread = kthread_run(vdec_recycle_thread, sess,
"vdec_recycle");
-
+ if (IS_ERR(sess->recycle_thread)) {
+ ret = PTR_ERR(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ goto err_poweroff;
+ }
+ }
schedule_work(&sess->esparser_queue_work);
return 0;
+err_poweroff:
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
err_free_vififo:
if (sess->vififo_vaddr) {
dma_free_coherent(sess->core->dev, sess->vififo_size,
@@ -464,7 +478,10 @@ static void vdec_stop_streaming(struct vb2_queue *q)
if (full_cleanup) {
if ((q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
!sess->streamon_out) && vdec_codec_needs_recycle(sess)) {
- kthread_stop(sess->recycle_thread);
+ if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
+ kthread_stop(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ }
}
vdec_poweroff(sess);
@@ -970,6 +987,31 @@ static int vdec_open(struct file *file)
static int vdec_close(struct file *file)
{
struct amvdec_session *sess = file_to_amvdec_session(file);
+ struct amvdec_core *core = sess->core;
+
+ if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
+ kthread_stop(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ }
+
+ mutex_lock(&core->lock);
+
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+ core->cur_sess = NULL;
+
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(core->dev, sess->vififo_size,
+ sess->vififo_vaddr, sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
+ kfree(sess->priv);
+ sess->priv = NULL;
+
+ mutex_unlock(&core->lock);
v4l2_m2m_ctx_release(sess->m2m_ctx);
v4l2_fh_del(&sess->fh, file);
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 04/19] media: meson: vdec: Fix use-after-free race between teardown and ISR routines
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:06 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
Prevent critical race conditions and null pointer dereferences inside
the Amlogic video decoder driver by synchronizing the interrupt service
routines during session teardown and file descriptor release phases.
The hardware interrupt handler (vdec_isr) and threaded handler
(vdec_threaded_isr) read 'core->cur_sess' without proper synchronization.
During a streaming teardown sequence via vdec_stop_streaming() or session
release inside vdec_close(), the underlying session structures can be
modified or freed concurrently. This creates a transient window where a
late-stage hardware interrupt can wake up, read 'core->cur_sess', and
dereference an invalid or NULL session pointer, causing a kernel panic.
Update both the hard and threaded interrupt service routines to capture
the current session reference using an smp_load_acquire() barrier snapshot.
If the snapshot resolves to NULL, terminate immediately with IRQ_HANDLED
to guarantee execution contexts do not process stale structures.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Suggested-by: Druk Tan Ozturk <doruk@0sec.ai>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 22 ++++++++++++++++++++--
drivers/staging/media/meson/vdec/vdec.h | 2 ++
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 7fc73d5cdebbf..7ae3d5a9dd6ab 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -484,6 +484,9 @@ static void vdec_stop_streaming(struct vb2_queue *q)
}
}
+ /* Synchronize and flush pending hardware interrupt service routines */
+ synchronize_irq(core->vdec_irq);
+
vdec_poweroff(sess);
vdec_free_canvas(sess);
@@ -989,6 +992,9 @@ static int vdec_close(struct file *file)
struct amvdec_session *sess = file_to_amvdec_session(file);
struct amvdec_core *core = sess->core;
+ /* Synchronize and flush pending hardware interrupt service routines */
+ synchronize_irq(core->vdec_irq);
+
if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
kthread_stop(sess->recycle_thread);
sess->recycle_thread = NULL;
@@ -1038,7 +1044,12 @@ static const struct v4l2_file_operations vdec_fops = {
static irqreturn_t vdec_isr(int irq, void *data)
{
struct amvdec_core *core = data;
- struct amvdec_session *sess = core->cur_sess;
+ struct amvdec_session *sess;
+
+ /* Secure an atomic acquire snapshot to protect against concurrent teardown */
+ sess = smp_load_acquire(&core->cur_sess);
+ if (!sess)
+ return IRQ_HANDLED;
sess->last_irq_jiffies = get_jiffies_64();
@@ -1048,7 +1059,12 @@ static irqreturn_t vdec_isr(int irq, void *data)
static irqreturn_t vdec_threaded_isr(int irq, void *data)
{
struct amvdec_core *core = data;
- struct amvdec_session *sess = core->cur_sess;
+ struct amvdec_session *sess;
+
+ /* Prevent late-stage threaded interrupts from dereferencing a NULL session */
+ sess = smp_load_acquire(&core->cur_sess);
+ if (!sess)
+ return IRQ_HANDLED;
return sess->fmt_out->codec_ops->threaded_isr(sess);
}
@@ -1136,6 +1152,8 @@ static int vdec_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ core->vdec_irq = irq;
+
ret = devm_request_threaded_irq(core->dev, irq, vdec_isr,
vdec_threaded_isr, IRQF_ONESHOT,
"vdec", core);
diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
index cc0cfafb8a951..d165c343fd022 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -67,6 +67,7 @@ struct amvdec_session;
* @v4l2_dev: v4l2 device
* @cur_sess: current decoding session
* @lock: video device lock
+ * @vdec_irq: irq for video decoding
*/
struct amvdec_core {
void __iomem *dos_base;
@@ -93,6 +94,7 @@ struct amvdec_core {
struct amvdec_session *cur_sess;
struct mutex lock;
+ int vdec_irq;
};
/**
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 04/19] media: meson: vdec: Fix use-after-free race between teardown and ISR routines
@ 2026-07-13 12:06 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne, Sashiko
Prevent critical race conditions and null pointer dereferences inside
the Amlogic video decoder driver by synchronizing the interrupt service
routines during session teardown and file descriptor release phases.
The hardware interrupt handler (vdec_isr) and threaded handler
(vdec_threaded_isr) read 'core->cur_sess' without proper synchronization.
During a streaming teardown sequence via vdec_stop_streaming() or session
release inside vdec_close(), the underlying session structures can be
modified or freed concurrently. This creates a transient window where a
late-stage hardware interrupt can wake up, read 'core->cur_sess', and
dereference an invalid or NULL session pointer, causing a kernel panic.
Update both the hard and threaded interrupt service routines to capture
the current session reference using an smp_load_acquire() barrier snapshot.
If the snapshot resolves to NULL, terminate immediately with IRQ_HANDLED
to guarantee execution contexts do not process stale structures.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Suggested-by: Druk Tan Ozturk <doruk@0sec.ai>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 22 ++++++++++++++++++++--
drivers/staging/media/meson/vdec/vdec.h | 2 ++
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 7fc73d5cdebbf..7ae3d5a9dd6ab 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -484,6 +484,9 @@ static void vdec_stop_streaming(struct vb2_queue *q)
}
}
+ /* Synchronize and flush pending hardware interrupt service routines */
+ synchronize_irq(core->vdec_irq);
+
vdec_poweroff(sess);
vdec_free_canvas(sess);
@@ -989,6 +992,9 @@ static int vdec_close(struct file *file)
struct amvdec_session *sess = file_to_amvdec_session(file);
struct amvdec_core *core = sess->core;
+ /* Synchronize and flush pending hardware interrupt service routines */
+ synchronize_irq(core->vdec_irq);
+
if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
kthread_stop(sess->recycle_thread);
sess->recycle_thread = NULL;
@@ -1038,7 +1044,12 @@ static const struct v4l2_file_operations vdec_fops = {
static irqreturn_t vdec_isr(int irq, void *data)
{
struct amvdec_core *core = data;
- struct amvdec_session *sess = core->cur_sess;
+ struct amvdec_session *sess;
+
+ /* Secure an atomic acquire snapshot to protect against concurrent teardown */
+ sess = smp_load_acquire(&core->cur_sess);
+ if (!sess)
+ return IRQ_HANDLED;
sess->last_irq_jiffies = get_jiffies_64();
@@ -1048,7 +1059,12 @@ static irqreturn_t vdec_isr(int irq, void *data)
static irqreturn_t vdec_threaded_isr(int irq, void *data)
{
struct amvdec_core *core = data;
- struct amvdec_session *sess = core->cur_sess;
+ struct amvdec_session *sess;
+
+ /* Prevent late-stage threaded interrupts from dereferencing a NULL session */
+ sess = smp_load_acquire(&core->cur_sess);
+ if (!sess)
+ return IRQ_HANDLED;
return sess->fmt_out->codec_ops->threaded_isr(sess);
}
@@ -1136,6 +1152,8 @@ static int vdec_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ core->vdec_irq = irq;
+
ret = devm_request_threaded_irq(core->dev, irq, vdec_isr,
vdec_threaded_isr, IRQF_ONESHOT,
"vdec", core);
diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
index cc0cfafb8a951..d165c343fd022 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -67,6 +67,7 @@ struct amvdec_session;
* @v4l2_dev: v4l2 device
* @cur_sess: current decoding session
* @lock: video device lock
+ * @vdec_irq: irq for video decoding
*/
struct amvdec_core {
void __iomem *dos_base;
@@ -93,6 +94,7 @@ struct amvdec_core {
struct amvdec_session *cur_sess;
struct mutex lock;
+ int vdec_irq;
};
/**
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 04/19] media: meson: vdec: Fix use-after-free race between teardown and ISR routines
@ 2026-07-13 12:06 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:06 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
Prevent critical race conditions and null pointer dereferences inside
the Amlogic video decoder driver by synchronizing the interrupt service
routines during session teardown and file descriptor release phases.
The hardware interrupt handler (vdec_isr) and threaded handler
(vdec_threaded_isr) read 'core->cur_sess' without proper synchronization.
During a streaming teardown sequence via vdec_stop_streaming() or session
release inside vdec_close(), the underlying session structures can be
modified or freed concurrently. This creates a transient window where a
late-stage hardware interrupt can wake up, read 'core->cur_sess', and
dereference an invalid or NULL session pointer, causing a kernel panic.
Update both the hard and threaded interrupt service routines to capture
the current session reference using an smp_load_acquire() barrier snapshot.
If the snapshot resolves to NULL, terminate immediately with IRQ_HANDLED
to guarantee execution contexts do not process stale structures.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Suggested-by: Druk Tan Ozturk <doruk@0sec.ai>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 22 ++++++++++++++++++++--
drivers/staging/media/meson/vdec/vdec.h | 2 ++
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 7fc73d5cdebbf..7ae3d5a9dd6ab 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -484,6 +484,9 @@ static void vdec_stop_streaming(struct vb2_queue *q)
}
}
+ /* Synchronize and flush pending hardware interrupt service routines */
+ synchronize_irq(core->vdec_irq);
+
vdec_poweroff(sess);
vdec_free_canvas(sess);
@@ -989,6 +992,9 @@ static int vdec_close(struct file *file)
struct amvdec_session *sess = file_to_amvdec_session(file);
struct amvdec_core *core = sess->core;
+ /* Synchronize and flush pending hardware interrupt service routines */
+ synchronize_irq(core->vdec_irq);
+
if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
kthread_stop(sess->recycle_thread);
sess->recycle_thread = NULL;
@@ -1038,7 +1044,12 @@ static const struct v4l2_file_operations vdec_fops = {
static irqreturn_t vdec_isr(int irq, void *data)
{
struct amvdec_core *core = data;
- struct amvdec_session *sess = core->cur_sess;
+ struct amvdec_session *sess;
+
+ /* Secure an atomic acquire snapshot to protect against concurrent teardown */
+ sess = smp_load_acquire(&core->cur_sess);
+ if (!sess)
+ return IRQ_HANDLED;
sess->last_irq_jiffies = get_jiffies_64();
@@ -1048,7 +1059,12 @@ static irqreturn_t vdec_isr(int irq, void *data)
static irqreturn_t vdec_threaded_isr(int irq, void *data)
{
struct amvdec_core *core = data;
- struct amvdec_session *sess = core->cur_sess;
+ struct amvdec_session *sess;
+
+ /* Prevent late-stage threaded interrupts from dereferencing a NULL session */
+ sess = smp_load_acquire(&core->cur_sess);
+ if (!sess)
+ return IRQ_HANDLED;
return sess->fmt_out->codec_ops->threaded_isr(sess);
}
@@ -1136,6 +1152,8 @@ static int vdec_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ core->vdec_irq = irq;
+
ret = devm_request_threaded_irq(core->dev, irq, vdec_isr,
vdec_threaded_isr, IRQF_ONESHOT,
"vdec", core);
diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
index cc0cfafb8a951..d165c343fd022 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -67,6 +67,7 @@ struct amvdec_session;
* @v4l2_dev: v4l2 device
* @cur_sess: current decoding session
* @lock: video device lock
+ * @vdec_irq: irq for video decoding
*/
struct amvdec_core {
void __iomem *dos_base;
@@ -93,6 +94,7 @@ struct amvdec_core {
struct amvdec_session *cur_sess;
struct mutex lock;
+ int vdec_irq;
};
/**
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 05/19] media: meson: vdec: Fix race condition and synchronize esparser IRQ
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Sashiko
During session teardown sequences in vdec_stop_streaming() and
vdec_close(), the 'esparser' hardware interrupt handler can still be
actively triggered or executing on another CPU core. This creates a
transient race condition where the ISR attempts to handle stream data and
allocate internal tracking state structures after session contexts have
been modified or freed.
Update esparser_isr() to read the current session context utilizing an
smp_load_acquire() barrier snapshot. If the pointer resolves to NULL,
terminate processing early with IRQ_HANDLED to protect against
concurrent dismantling.
Suggested-by: Doruk Tan Ozturk <doruk@0sec.ai>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 8 ++++++++
drivers/staging/media/meson/vdec/vdec.c | 4 ++++
drivers/staging/media/meson/vdec/vdec.h | 2 ++
3 files changed, 14 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index 4632346f04a9e..37749ede308c6 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -60,6 +60,12 @@ static irqreturn_t esparser_isr(int irq, void *dev)
{
int int_status;
struct amvdec_core *core = dev;
+ struct amvdec_session *sess;
+
+ /* Secure an atomic snapshot to protect against concurrent teardown */
+ sess = smp_load_acquire(&core->cur_sess);
+ if (!sess)
+ return IRQ_HANDLED;
int_status = amvdec_read_parser(core, PARSER_INT_STATUS);
amvdec_write_parser(core, PARSER_INT_STATUS, int_status);
@@ -439,6 +445,8 @@ int esparser_init(struct platform_device *pdev, struct amvdec_core *core)
if (irq < 0)
return irq;
+ core->esparser_irq = irq;
+
ret = devm_request_irq(dev, irq, esparser_isr, IRQF_SHARED,
"esparserirq", core);
if (ret) {
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 7ae3d5a9dd6ab..7689ffdb2e500 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -486,6 +486,8 @@ static void vdec_stop_streaming(struct vb2_queue *q)
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
+ /* Ensure esparser ISR finishes executing */
+ synchronize_irq(core->esparser_irq);
vdec_poweroff(sess);
vdec_free_canvas(sess);
@@ -994,6 +996,8 @@ static int vdec_close(struct file *file)
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
+ /* Ensure esparser ISR finishes executing */
+ synchronize_irq(core->esparser_irq);
if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
kthread_stop(sess->recycle_thread);
diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
index d165c343fd022..c4639cf33e73e 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -68,6 +68,7 @@ struct amvdec_session;
* @cur_sess: current decoding session
* @lock: video device lock
* @vdec_irq: irq for video decoding
+ * @esparser_irq: irq for elementary stream parsing
*/
struct amvdec_core {
void __iomem *dos_base;
@@ -95,6 +96,7 @@ struct amvdec_core {
struct amvdec_session *cur_sess;
struct mutex lock;
int vdec_irq;
+ int esparser_irq;
};
/**
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 05/19] media: meson: vdec: Fix race condition and synchronize esparser IRQ
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko
During session teardown sequences in vdec_stop_streaming() and
vdec_close(), the 'esparser' hardware interrupt handler can still be
actively triggered or executing on another CPU core. This creates a
transient race condition where the ISR attempts to handle stream data and
allocate internal tracking state structures after session contexts have
been modified or freed.
Update esparser_isr() to read the current session context utilizing an
smp_load_acquire() barrier snapshot. If the pointer resolves to NULL,
terminate processing early with IRQ_HANDLED to protect against
concurrent dismantling.
Suggested-by: Doruk Tan Ozturk <doruk@0sec.ai>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 8 ++++++++
drivers/staging/media/meson/vdec/vdec.c | 4 ++++
drivers/staging/media/meson/vdec/vdec.h | 2 ++
3 files changed, 14 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index 4632346f04a9e..37749ede308c6 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -60,6 +60,12 @@ static irqreturn_t esparser_isr(int irq, void *dev)
{
int int_status;
struct amvdec_core *core = dev;
+ struct amvdec_session *sess;
+
+ /* Secure an atomic snapshot to protect against concurrent teardown */
+ sess = smp_load_acquire(&core->cur_sess);
+ if (!sess)
+ return IRQ_HANDLED;
int_status = amvdec_read_parser(core, PARSER_INT_STATUS);
amvdec_write_parser(core, PARSER_INT_STATUS, int_status);
@@ -439,6 +445,8 @@ int esparser_init(struct platform_device *pdev, struct amvdec_core *core)
if (irq < 0)
return irq;
+ core->esparser_irq = irq;
+
ret = devm_request_irq(dev, irq, esparser_isr, IRQF_SHARED,
"esparserirq", core);
if (ret) {
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 7ae3d5a9dd6ab..7689ffdb2e500 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -486,6 +486,8 @@ static void vdec_stop_streaming(struct vb2_queue *q)
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
+ /* Ensure esparser ISR finishes executing */
+ synchronize_irq(core->esparser_irq);
vdec_poweroff(sess);
vdec_free_canvas(sess);
@@ -994,6 +996,8 @@ static int vdec_close(struct file *file)
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
+ /* Ensure esparser ISR finishes executing */
+ synchronize_irq(core->esparser_irq);
if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
kthread_stop(sess->recycle_thread);
diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
index d165c343fd022..c4639cf33e73e 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -68,6 +68,7 @@ struct amvdec_session;
* @cur_sess: current decoding session
* @lock: video device lock
* @vdec_irq: irq for video decoding
+ * @esparser_irq: irq for elementary stream parsing
*/
struct amvdec_core {
void __iomem *dos_base;
@@ -95,6 +96,7 @@ struct amvdec_core {
struct amvdec_session *cur_sess;
struct mutex lock;
int vdec_irq;
+ int esparser_irq;
};
/**
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 05/19] media: meson: vdec: Fix race condition and synchronize esparser IRQ
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko
During session teardown sequences in vdec_stop_streaming() and
vdec_close(), the 'esparser' hardware interrupt handler can still be
actively triggered or executing on another CPU core. This creates a
transient race condition where the ISR attempts to handle stream data and
allocate internal tracking state structures after session contexts have
been modified or freed.
Update esparser_isr() to read the current session context utilizing an
smp_load_acquire() barrier snapshot. If the pointer resolves to NULL,
terminate processing early with IRQ_HANDLED to protect against
concurrent dismantling.
Suggested-by: Doruk Tan Ozturk <doruk@0sec.ai>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 8 ++++++++
drivers/staging/media/meson/vdec/vdec.c | 4 ++++
drivers/staging/media/meson/vdec/vdec.h | 2 ++
3 files changed, 14 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index 4632346f04a9e..37749ede308c6 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -60,6 +60,12 @@ static irqreturn_t esparser_isr(int irq, void *dev)
{
int int_status;
struct amvdec_core *core = dev;
+ struct amvdec_session *sess;
+
+ /* Secure an atomic snapshot to protect against concurrent teardown */
+ sess = smp_load_acquire(&core->cur_sess);
+ if (!sess)
+ return IRQ_HANDLED;
int_status = amvdec_read_parser(core, PARSER_INT_STATUS);
amvdec_write_parser(core, PARSER_INT_STATUS, int_status);
@@ -439,6 +445,8 @@ int esparser_init(struct platform_device *pdev, struct amvdec_core *core)
if (irq < 0)
return irq;
+ core->esparser_irq = irq;
+
ret = devm_request_irq(dev, irq, esparser_isr, IRQF_SHARED,
"esparserirq", core);
if (ret) {
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 7ae3d5a9dd6ab..7689ffdb2e500 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -486,6 +486,8 @@ static void vdec_stop_streaming(struct vb2_queue *q)
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
+ /* Ensure esparser ISR finishes executing */
+ synchronize_irq(core->esparser_irq);
vdec_poweroff(sess);
vdec_free_canvas(sess);
@@ -994,6 +996,8 @@ static int vdec_close(struct file *file)
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
+ /* Ensure esparser ISR finishes executing */
+ synchronize_irq(core->esparser_irq);
if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
kthread_stop(sess->recycle_thread);
diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
index d165c343fd022..c4639cf33e73e 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -68,6 +68,7 @@ struct amvdec_session;
* @cur_sess: current decoding session
* @lock: video device lock
* @vdec_irq: irq for video decoding
+ * @esparser_irq: irq for elementary stream parsing
*/
struct amvdec_core {
void __iomem *dos_base;
@@ -95,6 +96,7 @@ struct amvdec_core {
struct amvdec_session *cur_sess;
struct mutex lock;
int vdec_irq;
+ int esparser_irq;
};
/**
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 06/19] media: meson: vdec: Fix race condition by canceling work sync
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne, Sashiko
Synchronize work-queue states cleanly during streaming teardown and
close operations to eliminate asynchronous pipeline race conditions.
The esparser ISR can schedule work onto 'esparser_queue_work'. While
synchronize_irq() ensures the ISR itself finishes executing, it does
not prevent any already scheduled work items from running concurrently
during driver teardown. This causes a race condition during stream
stopping or file closing.
Remove the scheduling of esparser queue work during destination buffer
completion, as freeing the vififo is handled elsewhere. Add synchronous
cancellation of any pending work in vdec_stop_streaming and vdec_close
before synchronizing interrupts to prevent use-after-free and race
conditions.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 7689ffdb2e500..6fe9722577179 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -484,11 +484,15 @@ static void vdec_stop_streaming(struct vb2_queue *q)
}
}
+ mutex_unlock(&core->lock);
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
/* Ensure esparser ISR finishes executing */
synchronize_irq(core->esparser_irq);
+ cancel_work_sync(&sess->esparser_queue_work);
+ mutex_lock(&core->lock);
+
vdec_poweroff(sess);
vdec_free_canvas(sess);
@@ -999,6 +1003,8 @@ static int vdec_close(struct file *file)
/* Ensure esparser ISR finishes executing */
synchronize_irq(core->esparser_irq);
+ cancel_work_sync(&sess->esparser_queue_work);
+
if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
kthread_stop(sess->recycle_thread);
sess->recycle_thread = NULL;
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 06/19] media: meson: vdec: Fix race condition by canceling work sync
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
Synchronize work-queue states cleanly during streaming teardown and
close operations to eliminate asynchronous pipeline race conditions.
The esparser ISR can schedule work onto 'esparser_queue_work'. While
synchronize_irq() ensures the ISR itself finishes executing, it does
not prevent any already scheduled work items from running concurrently
during driver teardown. This causes a race condition during stream
stopping or file closing.
Remove the scheduling of esparser queue work during destination buffer
completion, as freeing the vififo is handled elsewhere. Add synchronous
cancellation of any pending work in vdec_stop_streaming and vdec_close
before synchronizing interrupts to prevent use-after-free and race
conditions.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 7689ffdb2e500..6fe9722577179 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -484,11 +484,15 @@ static void vdec_stop_streaming(struct vb2_queue *q)
}
}
+ mutex_unlock(&core->lock);
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
/* Ensure esparser ISR finishes executing */
synchronize_irq(core->esparser_irq);
+ cancel_work_sync(&sess->esparser_queue_work);
+ mutex_lock(&core->lock);
+
vdec_poweroff(sess);
vdec_free_canvas(sess);
@@ -999,6 +1003,8 @@ static int vdec_close(struct file *file)
/* Ensure esparser ISR finishes executing */
synchronize_irq(core->esparser_irq);
+ cancel_work_sync(&sess->esparser_queue_work);
+
if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
kthread_stop(sess->recycle_thread);
sess->recycle_thread = NULL;
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 06/19] media: meson: vdec: Fix race condition by canceling work sync
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
Synchronize work-queue states cleanly during streaming teardown and
close operations to eliminate asynchronous pipeline race conditions.
The esparser ISR can schedule work onto 'esparser_queue_work'. While
synchronize_irq() ensures the ISR itself finishes executing, it does
not prevent any already scheduled work items from running concurrently
during driver teardown. This causes a race condition during stream
stopping or file closing.
Remove the scheduling of esparser queue work during destination buffer
completion, as freeing the vififo is handled elsewhere. Add synchronous
cancellation of any pending work in vdec_stop_streaming and vdec_close
before synchronizing interrupts to prevent use-after-free and race
conditions.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 7689ffdb2e500..6fe9722577179 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -484,11 +484,15 @@ static void vdec_stop_streaming(struct vb2_queue *q)
}
}
+ mutex_unlock(&core->lock);
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
/* Ensure esparser ISR finishes executing */
synchronize_irq(core->esparser_irq);
+ cancel_work_sync(&sess->esparser_queue_work);
+ mutex_lock(&core->lock);
+
vdec_poweroff(sess);
vdec_free_canvas(sess);
@@ -999,6 +1003,8 @@ static int vdec_close(struct file *file)
/* Ensure esparser ISR finishes executing */
synchronize_irq(core->esparser_irq);
+ cancel_work_sync(&sess->esparser_queue_work);
+
if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
kthread_stop(sess->recycle_thread);
sess->recycle_thread = NULL;
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 07/19] media: meson: vdec: Refactor esparser work queue and fix teardown race
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne
Rework the esparser work queue logic to prevent concurrent queue mutations
and fix a race condition during streaming teardown.
The previous implementation relied on v4l2_m2m_for_each_src_buf_safe(),
which held the session mutex across the entire queue traversal. This
blocked concurrent operations, caused inaccurate buffer mapping, and
triggered 100+ second watchdog freezes due to an unyielding polling loop.
Additionally, the low-level parser handled buffer ownership adjustments
and completion callbacks directly, leading to inconsistent error paths and
races between streamon and streamoff.
Fix this by utilizing the modern scoped_guard(mutex) mechanism inside the
main worker loop. This allows the session lock to be automatically
dropped on any conditional breakout path, preventing voluntary scheduling
bugs while invoking cond_resched(). De-couple buffer lifecycles from the
hardware parser routine by leveraging safe, linear fetches via
v4l2_m2m_next_src_buf() and v4l2_m2m_src_buf_remove(), ensuring orderly
frame transactions. Refactor esparser_queue() to return explicit error
codes (such as -EBADMSG, -EIO, and -EAGAIN) to safely handle buffer states.
Clean up teardown paths: Execute cancel_work_sync() inside
vdec_stop_streaming() and vdec_close() immediately after
synchronize_irq(). This ensures any pending scheduled queue work is fully
flushed and canceled before the driver destroys hardware instances.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 69 +++++++++++++++------
1 file changed, 49 insertions(+), 20 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index 37749ede308c6..e5d3d817b9b2b 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -328,15 +328,11 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
return -EAGAIN;
}
- v4l2_m2m_src_buf_remove_by_buf(sess->m2m_ctx, vbuf);
-
offset = esparser_get_offset(sess);
ret = amvdec_add_ts(sess, vb->timestamp, vbuf->timecode, offset, vbuf->flags);
- if (ret) {
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ if (ret)
return ret;
- }
dev_dbg(core->dev, "esparser: ts = %llu pld_size = %u offset = %08X flags = %08X\n",
vb->timestamp, payload_size, offset, vbuf->flags);
@@ -348,12 +344,10 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
payload_size = vp9_update_header(core, vb);
- /* If unable to alter buffer to add headers */
if (payload_size == 0) {
+ dev_err(core->dev, "esparser: VP9 header update failed\n");
amvdec_remove_ts(sess, vb->timestamp);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
-
- return 0;
+ return -EBADMSG;
}
}
@@ -363,33 +357,68 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
if (ret <= 0) {
dev_warn(core->dev, "esparser: input parsing error\n");
amvdec_remove_ts(sess, vb->timestamp);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
amvdec_write_parser(core, PARSER_FETCH_CMD, 0);
-
- return 0;
+ return -EIO;
}
atomic_inc(&sess->esparser_queued_bufs);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
return 0;
}
void esparser_queue_all_src(struct work_struct *work)
{
- struct v4l2_m2m_buffer *buf, *n;
struct amvdec_session *sess =
container_of(work, struct amvdec_session, esparser_queue_work);
+ struct device *dev = sess->core->dev_dec;
+ int ret;
- mutex_lock(&sess->lock);
- v4l2_m2m_for_each_src_buf_safe(sess->m2m_ctx, buf, n) {
- if (sess->should_stop)
- break;
+ while (1) {
+ struct vb2_v4l2_buffer *vbuf = NULL;
+ bool processed_frame = false;
+
+ scoped_guard(mutex, &sess->lock) {
+ /* Safe atomic tracking check: exit loop if session is shutting down */
+ if (sess->should_stop)
+ return;
+
+ /* Queue completely empty: exit work loop cleanly */
+ vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
+ if (!vbuf)
+ break;
+
+ /* Stop processing if we hit the end-of-stream drain buffer */
+ if (vbuf->flags & V4L2_BUF_FLAG_LAST)
+ break;
+
+ /* Check hardware FIFO limits safely inside the locks */
+ ret = esparser_queue(sess, vbuf);
+ if (ret == -EAGAIN)
+ break;
+
+ /* Pop the buffer from the source queue since it is now processed */
+ vbuf = v4l2_m2m_src_buf_remove(sess->m2m_ctx);
+ if (!vbuf) {
+ dev_dbg(dev, "Buffer missing during queue removal\n");
+ break;
+ }
+
+ /* Complete the buffer transaction based on parser results */
+ if (ret < 0)
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ else
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
- if (esparser_queue(sess, &buf->vb) < 0)
+ /* Set tracking flag indicating transaction completion */
+ processed_frame = true;
+ }
+
+ if (processed_frame)
break;
+
+ /* Give other threads and IRQ routines a window to execute while unlocked */
+ cond_resched();
}
- mutex_unlock(&sess->lock);
}
int esparser_power_up(struct amvdec_session *sess)
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 07/19] media: meson: vdec: Refactor esparser work queue and fix teardown race
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Rework the esparser work queue logic to prevent concurrent queue mutations
and fix a race condition during streaming teardown.
The previous implementation relied on v4l2_m2m_for_each_src_buf_safe(),
which held the session mutex across the entire queue traversal. This
blocked concurrent operations, caused inaccurate buffer mapping, and
triggered 100+ second watchdog freezes due to an unyielding polling loop.
Additionally, the low-level parser handled buffer ownership adjustments
and completion callbacks directly, leading to inconsistent error paths and
races between streamon and streamoff.
Fix this by utilizing the modern scoped_guard(mutex) mechanism inside the
main worker loop. This allows the session lock to be automatically
dropped on any conditional breakout path, preventing voluntary scheduling
bugs while invoking cond_resched(). De-couple buffer lifecycles from the
hardware parser routine by leveraging safe, linear fetches via
v4l2_m2m_next_src_buf() and v4l2_m2m_src_buf_remove(), ensuring orderly
frame transactions. Refactor esparser_queue() to return explicit error
codes (such as -EBADMSG, -EIO, and -EAGAIN) to safely handle buffer states.
Clean up teardown paths: Execute cancel_work_sync() inside
vdec_stop_streaming() and vdec_close() immediately after
synchronize_irq(). This ensures any pending scheduled queue work is fully
flushed and canceled before the driver destroys hardware instances.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 69 +++++++++++++++------
1 file changed, 49 insertions(+), 20 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index 37749ede308c6..e5d3d817b9b2b 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -328,15 +328,11 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
return -EAGAIN;
}
- v4l2_m2m_src_buf_remove_by_buf(sess->m2m_ctx, vbuf);
-
offset = esparser_get_offset(sess);
ret = amvdec_add_ts(sess, vb->timestamp, vbuf->timecode, offset, vbuf->flags);
- if (ret) {
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ if (ret)
return ret;
- }
dev_dbg(core->dev, "esparser: ts = %llu pld_size = %u offset = %08X flags = %08X\n",
vb->timestamp, payload_size, offset, vbuf->flags);
@@ -348,12 +344,10 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
payload_size = vp9_update_header(core, vb);
- /* If unable to alter buffer to add headers */
if (payload_size == 0) {
+ dev_err(core->dev, "esparser: VP9 header update failed\n");
amvdec_remove_ts(sess, vb->timestamp);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
-
- return 0;
+ return -EBADMSG;
}
}
@@ -363,33 +357,68 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
if (ret <= 0) {
dev_warn(core->dev, "esparser: input parsing error\n");
amvdec_remove_ts(sess, vb->timestamp);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
amvdec_write_parser(core, PARSER_FETCH_CMD, 0);
-
- return 0;
+ return -EIO;
}
atomic_inc(&sess->esparser_queued_bufs);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
return 0;
}
void esparser_queue_all_src(struct work_struct *work)
{
- struct v4l2_m2m_buffer *buf, *n;
struct amvdec_session *sess =
container_of(work, struct amvdec_session, esparser_queue_work);
+ struct device *dev = sess->core->dev_dec;
+ int ret;
- mutex_lock(&sess->lock);
- v4l2_m2m_for_each_src_buf_safe(sess->m2m_ctx, buf, n) {
- if (sess->should_stop)
- break;
+ while (1) {
+ struct vb2_v4l2_buffer *vbuf = NULL;
+ bool processed_frame = false;
+
+ scoped_guard(mutex, &sess->lock) {
+ /* Safe atomic tracking check: exit loop if session is shutting down */
+ if (sess->should_stop)
+ return;
+
+ /* Queue completely empty: exit work loop cleanly */
+ vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
+ if (!vbuf)
+ break;
+
+ /* Stop processing if we hit the end-of-stream drain buffer */
+ if (vbuf->flags & V4L2_BUF_FLAG_LAST)
+ break;
+
+ /* Check hardware FIFO limits safely inside the locks */
+ ret = esparser_queue(sess, vbuf);
+ if (ret == -EAGAIN)
+ break;
+
+ /* Pop the buffer from the source queue since it is now processed */
+ vbuf = v4l2_m2m_src_buf_remove(sess->m2m_ctx);
+ if (!vbuf) {
+ dev_dbg(dev, "Buffer missing during queue removal\n");
+ break;
+ }
+
+ /* Complete the buffer transaction based on parser results */
+ if (ret < 0)
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ else
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
- if (esparser_queue(sess, &buf->vb) < 0)
+ /* Set tracking flag indicating transaction completion */
+ processed_frame = true;
+ }
+
+ if (processed_frame)
break;
+
+ /* Give other threads and IRQ routines a window to execute while unlocked */
+ cond_resched();
}
- mutex_unlock(&sess->lock);
}
int esparser_power_up(struct amvdec_session *sess)
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 07/19] media: meson: vdec: Refactor esparser work queue and fix teardown race
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Rework the esparser work queue logic to prevent concurrent queue mutations
and fix a race condition during streaming teardown.
The previous implementation relied on v4l2_m2m_for_each_src_buf_safe(),
which held the session mutex across the entire queue traversal. This
blocked concurrent operations, caused inaccurate buffer mapping, and
triggered 100+ second watchdog freezes due to an unyielding polling loop.
Additionally, the low-level parser handled buffer ownership adjustments
and completion callbacks directly, leading to inconsistent error paths and
races between streamon and streamoff.
Fix this by utilizing the modern scoped_guard(mutex) mechanism inside the
main worker loop. This allows the session lock to be automatically
dropped on any conditional breakout path, preventing voluntary scheduling
bugs while invoking cond_resched(). De-couple buffer lifecycles from the
hardware parser routine by leveraging safe, linear fetches via
v4l2_m2m_next_src_buf() and v4l2_m2m_src_buf_remove(), ensuring orderly
frame transactions. Refactor esparser_queue() to return explicit error
codes (such as -EBADMSG, -EIO, and -EAGAIN) to safely handle buffer states.
Clean up teardown paths: Execute cancel_work_sync() inside
vdec_stop_streaming() and vdec_close() immediately after
synchronize_irq(). This ensures any pending scheduled queue work is fully
flushed and canceled before the driver destroys hardware instances.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 69 +++++++++++++++------
1 file changed, 49 insertions(+), 20 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index 37749ede308c6..e5d3d817b9b2b 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -328,15 +328,11 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
return -EAGAIN;
}
- v4l2_m2m_src_buf_remove_by_buf(sess->m2m_ctx, vbuf);
-
offset = esparser_get_offset(sess);
ret = amvdec_add_ts(sess, vb->timestamp, vbuf->timecode, offset, vbuf->flags);
- if (ret) {
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ if (ret)
return ret;
- }
dev_dbg(core->dev, "esparser: ts = %llu pld_size = %u offset = %08X flags = %08X\n",
vb->timestamp, payload_size, offset, vbuf->flags);
@@ -348,12 +344,10 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
payload_size = vp9_update_header(core, vb);
- /* If unable to alter buffer to add headers */
if (payload_size == 0) {
+ dev_err(core->dev, "esparser: VP9 header update failed\n");
amvdec_remove_ts(sess, vb->timestamp);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
-
- return 0;
+ return -EBADMSG;
}
}
@@ -363,33 +357,68 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
if (ret <= 0) {
dev_warn(core->dev, "esparser: input parsing error\n");
amvdec_remove_ts(sess, vb->timestamp);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
amvdec_write_parser(core, PARSER_FETCH_CMD, 0);
-
- return 0;
+ return -EIO;
}
atomic_inc(&sess->esparser_queued_bufs);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
return 0;
}
void esparser_queue_all_src(struct work_struct *work)
{
- struct v4l2_m2m_buffer *buf, *n;
struct amvdec_session *sess =
container_of(work, struct amvdec_session, esparser_queue_work);
+ struct device *dev = sess->core->dev_dec;
+ int ret;
- mutex_lock(&sess->lock);
- v4l2_m2m_for_each_src_buf_safe(sess->m2m_ctx, buf, n) {
- if (sess->should_stop)
- break;
+ while (1) {
+ struct vb2_v4l2_buffer *vbuf = NULL;
+ bool processed_frame = false;
+
+ scoped_guard(mutex, &sess->lock) {
+ /* Safe atomic tracking check: exit loop if session is shutting down */
+ if (sess->should_stop)
+ return;
+
+ /* Queue completely empty: exit work loop cleanly */
+ vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
+ if (!vbuf)
+ break;
+
+ /* Stop processing if we hit the end-of-stream drain buffer */
+ if (vbuf->flags & V4L2_BUF_FLAG_LAST)
+ break;
+
+ /* Check hardware FIFO limits safely inside the locks */
+ ret = esparser_queue(sess, vbuf);
+ if (ret == -EAGAIN)
+ break;
+
+ /* Pop the buffer from the source queue since it is now processed */
+ vbuf = v4l2_m2m_src_buf_remove(sess->m2m_ctx);
+ if (!vbuf) {
+ dev_dbg(dev, "Buffer missing during queue removal\n");
+ break;
+ }
+
+ /* Complete the buffer transaction based on parser results */
+ if (ret < 0)
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ else
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
- if (esparser_queue(sess, &buf->vb) < 0)
+ /* Set tracking flag indicating transaction completion */
+ processed_frame = true;
+ }
+
+ if (processed_frame)
break;
+
+ /* Give other threads and IRQ routines a window to execute while unlocked */
+ cond_resched();
}
- mutex_unlock(&sess->lock);
}
int esparser_power_up(struct amvdec_session *sess)
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 08/19] media: meson: vdec: Fix concurrent execution races and unsafe teardown
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne
Address data races involving 'should_stop' and prevent multi-session
hardware clobbering by enforcing atomic tracking and strict owner
validation during device teardown.
The esparser work queue reads 'sess->should_stop' outside of critical
regions without serialization primitives, risking data races or visibility
delays. Furthermore, vdec_close() and vdec_stop_streaming() blindly
shut down hardware components (via poweroff and canvas frees) and nullify
'core->cur_sess' without confirming that the executing session actually
owns the active hardware context. In multi-session scenarios, this allows
a closing inactive session to inadvertently break a running session.
To fix these synchronization and lifecycle issues with the following
changes use thread-safe flagging: Wrap reads and writes of
'sess->should_stop' in READ_ONCE() and WRITE_ONCE() to prevent compiler
optimizations from caching the condition variables across scheduling
boundaries and optimizations across workqueue execution threads.
Also safe context releasing: transition 'core->cur_sess' pointer
clearings to use smp_store_release(). This ensures all prior internal
memory structures are entirely flushed and visible to other execution
cores.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 2 +-
drivers/staging/media/meson/vdec/vdec.c | 88 ++++++++++++++-------
2 files changed, 59 insertions(+), 31 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index e5d3d817b9b2b..959673742e699 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -379,7 +379,7 @@ void esparser_queue_all_src(struct work_struct *work)
scoped_guard(mutex, &sess->lock) {
/* Safe atomic tracking check: exit loop if session is shutting down */
- if (sess->should_stop)
+ if (READ_ONCE(sess->should_stop))
return;
/* Queue completely empty: exit work loop cleanly */
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 6fe9722577179..83a9b1238972a 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -287,9 +287,13 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
struct amvdec_session *sess = vb2_get_drv_priv(q);
struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
struct amvdec_core *core = sess->core;
+ struct device *dev = core->dev_dec;
struct vb2_v4l2_buffer *buf;
int ret;
+ /* Reset workqueue loop shutdown signal to allow streaming */
+ WRITE_ONCE(sess->should_stop, 0);
+
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
sess->streamon_out = 1;
else
@@ -336,7 +340,7 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
dma_alloc_coherent(sess->core->dev, sess->vififo_size,
&sess->vififo_paddr, GFP_KERNEL);
if (!sess->vififo_vaddr) {
- dev_err(sess->core->dev, "Failed to request VIFIFO buffer\n");
+ dev_err(dev, "Failed to request VIFIFO buffer\n");
ret = -ENOMEM;
goto err_cleanup_session;
}
@@ -388,10 +392,12 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
sess->streamon_cap = 0;
mutex_lock(&core->lock);
- if (core->cur_sess == sess)
- core->cur_sess = NULL;
- if (sess->status != STATUS_NEEDS_RESUME)
- sess->status = STATUS_STOPPED;
+ if (core->cur_sess == sess) {
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+ if (sess->status != STATUS_NEEDS_RESUME)
+ sess->status = STATUS_STOPPED;
+ }
mutex_unlock(&core->lock);
err_unlock_no_hw:
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
@@ -441,6 +447,9 @@ static void vdec_stop_streaming(struct vb2_queue *q)
enum amvdec_status old_status;
bool full_cleanup = false;
+ /* Signal workqueue loop to abort instantly */
+ WRITE_ONCE(sess->should_stop, 1);
+
/* flush buffers to kill background workqueue thread */
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
@@ -493,26 +502,33 @@ static void vdec_stop_streaming(struct vb2_queue *q)
cancel_work_sync(&sess->esparser_queue_work);
mutex_lock(&core->lock);
- vdec_poweroff(sess);
- vdec_free_canvas(sess);
+ if (core->cur_sess == sess) {
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(sess->core->dev,
+ sess->vififo_size,
+ sess->vififo_vaddr,
+ sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
- if (sess->vififo_vaddr) {
- dma_free_coherent(sess->core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
- sess->vififo_vaddr = NULL;
- sess->vififo_paddr = 0;
- }
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
- vdec_reset_timestamps(sess);
- vdec_reset_bufs_recycle(sess);
- core->cur_sess = NULL;
+ kfree(sess->priv);
+ sess->priv = NULL;
- kfree(sess->priv);
- sess->priv = NULL;
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+ }
} else {
if (sess->status == STATUS_NEEDS_RESUME)
sess->changed_format = 0;
}
+
mutex_unlock(&core->lock);
}
@@ -802,7 +818,7 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
if (cmd->cmd == V4L2_DEC_CMD_START) {
v4l2_m2m_clear_state(sess->m2m_ctx);
- sess->should_stop = 0;
+ WRITE_ONCE(sess->should_stop, 0);
return 0;
}
@@ -812,7 +828,7 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
dev_dbg(dev, "Received V4L2_DEC_CMD_STOP\n");
- sess->should_stop = 1;
+ WRITE_ONCE(sess->should_stop, 1);
v4l2_m2m_mark_stopped(sess->m2m_ctx);
@@ -998,6 +1014,9 @@ static int vdec_close(struct file *file)
struct amvdec_session *sess = file_to_amvdec_session(file);
struct amvdec_core *core = sess->core;
+ /* Signal workqueue loop to abort instantly */
+ WRITE_ONCE(sess->should_stop, 1);
+
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
/* Ensure esparser ISR finishes executing */
@@ -1012,21 +1031,30 @@ static int vdec_close(struct file *file)
mutex_lock(&core->lock);
- vdec_poweroff(sess);
- vdec_free_canvas(sess);
- core->cur_sess = NULL;
+ if (core->cur_sess == sess) {
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
- if (sess->vififo_vaddr) {
- dma_free_coherent(core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
- sess->vififo_vaddr = NULL;
- sess->vififo_paddr = 0;
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(core->dev,
+ sess->vififo_size,
+ sess->vififo_vaddr,
+ sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
}
- vdec_reset_timestamps(sess);
- vdec_reset_bufs_recycle(sess);
+
kfree(sess->priv);
sess->priv = NULL;
+ /* Unconditionally set our local status to stopped */
+ sess->status = STATUS_STOPPED;
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+
mutex_unlock(&core->lock);
v4l2_m2m_ctx_release(sess->m2m_ctx);
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 08/19] media: meson: vdec: Fix concurrent execution races and unsafe teardown
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Address data races involving 'should_stop' and prevent multi-session
hardware clobbering by enforcing atomic tracking and strict owner
validation during device teardown.
The esparser work queue reads 'sess->should_stop' outside of critical
regions without serialization primitives, risking data races or visibility
delays. Furthermore, vdec_close() and vdec_stop_streaming() blindly
shut down hardware components (via poweroff and canvas frees) and nullify
'core->cur_sess' without confirming that the executing session actually
owns the active hardware context. In multi-session scenarios, this allows
a closing inactive session to inadvertently break a running session.
To fix these synchronization and lifecycle issues with the following
changes use thread-safe flagging: Wrap reads and writes of
'sess->should_stop' in READ_ONCE() and WRITE_ONCE() to prevent compiler
optimizations from caching the condition variables across scheduling
boundaries and optimizations across workqueue execution threads.
Also safe context releasing: transition 'core->cur_sess' pointer
clearings to use smp_store_release(). This ensures all prior internal
memory structures are entirely flushed and visible to other execution
cores.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 2 +-
drivers/staging/media/meson/vdec/vdec.c | 88 ++++++++++++++-------
2 files changed, 59 insertions(+), 31 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index e5d3d817b9b2b..959673742e699 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -379,7 +379,7 @@ void esparser_queue_all_src(struct work_struct *work)
scoped_guard(mutex, &sess->lock) {
/* Safe atomic tracking check: exit loop if session is shutting down */
- if (sess->should_stop)
+ if (READ_ONCE(sess->should_stop))
return;
/* Queue completely empty: exit work loop cleanly */
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 6fe9722577179..83a9b1238972a 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -287,9 +287,13 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
struct amvdec_session *sess = vb2_get_drv_priv(q);
struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
struct amvdec_core *core = sess->core;
+ struct device *dev = core->dev_dec;
struct vb2_v4l2_buffer *buf;
int ret;
+ /* Reset workqueue loop shutdown signal to allow streaming */
+ WRITE_ONCE(sess->should_stop, 0);
+
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
sess->streamon_out = 1;
else
@@ -336,7 +340,7 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
dma_alloc_coherent(sess->core->dev, sess->vififo_size,
&sess->vififo_paddr, GFP_KERNEL);
if (!sess->vififo_vaddr) {
- dev_err(sess->core->dev, "Failed to request VIFIFO buffer\n");
+ dev_err(dev, "Failed to request VIFIFO buffer\n");
ret = -ENOMEM;
goto err_cleanup_session;
}
@@ -388,10 +392,12 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
sess->streamon_cap = 0;
mutex_lock(&core->lock);
- if (core->cur_sess == sess)
- core->cur_sess = NULL;
- if (sess->status != STATUS_NEEDS_RESUME)
- sess->status = STATUS_STOPPED;
+ if (core->cur_sess == sess) {
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+ if (sess->status != STATUS_NEEDS_RESUME)
+ sess->status = STATUS_STOPPED;
+ }
mutex_unlock(&core->lock);
err_unlock_no_hw:
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
@@ -441,6 +447,9 @@ static void vdec_stop_streaming(struct vb2_queue *q)
enum amvdec_status old_status;
bool full_cleanup = false;
+ /* Signal workqueue loop to abort instantly */
+ WRITE_ONCE(sess->should_stop, 1);
+
/* flush buffers to kill background workqueue thread */
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
@@ -493,26 +502,33 @@ static void vdec_stop_streaming(struct vb2_queue *q)
cancel_work_sync(&sess->esparser_queue_work);
mutex_lock(&core->lock);
- vdec_poweroff(sess);
- vdec_free_canvas(sess);
+ if (core->cur_sess == sess) {
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(sess->core->dev,
+ sess->vififo_size,
+ sess->vififo_vaddr,
+ sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
- if (sess->vififo_vaddr) {
- dma_free_coherent(sess->core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
- sess->vififo_vaddr = NULL;
- sess->vififo_paddr = 0;
- }
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
- vdec_reset_timestamps(sess);
- vdec_reset_bufs_recycle(sess);
- core->cur_sess = NULL;
+ kfree(sess->priv);
+ sess->priv = NULL;
- kfree(sess->priv);
- sess->priv = NULL;
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+ }
} else {
if (sess->status == STATUS_NEEDS_RESUME)
sess->changed_format = 0;
}
+
mutex_unlock(&core->lock);
}
@@ -802,7 +818,7 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
if (cmd->cmd == V4L2_DEC_CMD_START) {
v4l2_m2m_clear_state(sess->m2m_ctx);
- sess->should_stop = 0;
+ WRITE_ONCE(sess->should_stop, 0);
return 0;
}
@@ -812,7 +828,7 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
dev_dbg(dev, "Received V4L2_DEC_CMD_STOP\n");
- sess->should_stop = 1;
+ WRITE_ONCE(sess->should_stop, 1);
v4l2_m2m_mark_stopped(sess->m2m_ctx);
@@ -998,6 +1014,9 @@ static int vdec_close(struct file *file)
struct amvdec_session *sess = file_to_amvdec_session(file);
struct amvdec_core *core = sess->core;
+ /* Signal workqueue loop to abort instantly */
+ WRITE_ONCE(sess->should_stop, 1);
+
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
/* Ensure esparser ISR finishes executing */
@@ -1012,21 +1031,30 @@ static int vdec_close(struct file *file)
mutex_lock(&core->lock);
- vdec_poweroff(sess);
- vdec_free_canvas(sess);
- core->cur_sess = NULL;
+ if (core->cur_sess == sess) {
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
- if (sess->vififo_vaddr) {
- dma_free_coherent(core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
- sess->vififo_vaddr = NULL;
- sess->vififo_paddr = 0;
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(core->dev,
+ sess->vififo_size,
+ sess->vififo_vaddr,
+ sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
}
- vdec_reset_timestamps(sess);
- vdec_reset_bufs_recycle(sess);
+
kfree(sess->priv);
sess->priv = NULL;
+ /* Unconditionally set our local status to stopped */
+ sess->status = STATUS_STOPPED;
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+
mutex_unlock(&core->lock);
v4l2_m2m_ctx_release(sess->m2m_ctx);
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 08/19] media: meson: vdec: Fix concurrent execution races and unsafe teardown
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Address data races involving 'should_stop' and prevent multi-session
hardware clobbering by enforcing atomic tracking and strict owner
validation during device teardown.
The esparser work queue reads 'sess->should_stop' outside of critical
regions without serialization primitives, risking data races or visibility
delays. Furthermore, vdec_close() and vdec_stop_streaming() blindly
shut down hardware components (via poweroff and canvas frees) and nullify
'core->cur_sess' without confirming that the executing session actually
owns the active hardware context. In multi-session scenarios, this allows
a closing inactive session to inadvertently break a running session.
To fix these synchronization and lifecycle issues with the following
changes use thread-safe flagging: Wrap reads and writes of
'sess->should_stop' in READ_ONCE() and WRITE_ONCE() to prevent compiler
optimizations from caching the condition variables across scheduling
boundaries and optimizations across workqueue execution threads.
Also safe context releasing: transition 'core->cur_sess' pointer
clearings to use smp_store_release(). This ensures all prior internal
memory structures are entirely flushed and visible to other execution
cores.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 2 +-
drivers/staging/media/meson/vdec/vdec.c | 88 ++++++++++++++-------
2 files changed, 59 insertions(+), 31 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index e5d3d817b9b2b..959673742e699 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -379,7 +379,7 @@ void esparser_queue_all_src(struct work_struct *work)
scoped_guard(mutex, &sess->lock) {
/* Safe atomic tracking check: exit loop if session is shutting down */
- if (sess->should_stop)
+ if (READ_ONCE(sess->should_stop))
return;
/* Queue completely empty: exit work loop cleanly */
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 6fe9722577179..83a9b1238972a 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -287,9 +287,13 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
struct amvdec_session *sess = vb2_get_drv_priv(q);
struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
struct amvdec_core *core = sess->core;
+ struct device *dev = core->dev_dec;
struct vb2_v4l2_buffer *buf;
int ret;
+ /* Reset workqueue loop shutdown signal to allow streaming */
+ WRITE_ONCE(sess->should_stop, 0);
+
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
sess->streamon_out = 1;
else
@@ -336,7 +340,7 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
dma_alloc_coherent(sess->core->dev, sess->vififo_size,
&sess->vififo_paddr, GFP_KERNEL);
if (!sess->vififo_vaddr) {
- dev_err(sess->core->dev, "Failed to request VIFIFO buffer\n");
+ dev_err(dev, "Failed to request VIFIFO buffer\n");
ret = -ENOMEM;
goto err_cleanup_session;
}
@@ -388,10 +392,12 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
sess->streamon_cap = 0;
mutex_lock(&core->lock);
- if (core->cur_sess == sess)
- core->cur_sess = NULL;
- if (sess->status != STATUS_NEEDS_RESUME)
- sess->status = STATUS_STOPPED;
+ if (core->cur_sess == sess) {
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+ if (sess->status != STATUS_NEEDS_RESUME)
+ sess->status = STATUS_STOPPED;
+ }
mutex_unlock(&core->lock);
err_unlock_no_hw:
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
@@ -441,6 +447,9 @@ static void vdec_stop_streaming(struct vb2_queue *q)
enum amvdec_status old_status;
bool full_cleanup = false;
+ /* Signal workqueue loop to abort instantly */
+ WRITE_ONCE(sess->should_stop, 1);
+
/* flush buffers to kill background workqueue thread */
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
@@ -493,26 +502,33 @@ static void vdec_stop_streaming(struct vb2_queue *q)
cancel_work_sync(&sess->esparser_queue_work);
mutex_lock(&core->lock);
- vdec_poweroff(sess);
- vdec_free_canvas(sess);
+ if (core->cur_sess == sess) {
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
+
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(sess->core->dev,
+ sess->vififo_size,
+ sess->vififo_vaddr,
+ sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
- if (sess->vififo_vaddr) {
- dma_free_coherent(sess->core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
- sess->vififo_vaddr = NULL;
- sess->vififo_paddr = 0;
- }
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
- vdec_reset_timestamps(sess);
- vdec_reset_bufs_recycle(sess);
- core->cur_sess = NULL;
+ kfree(sess->priv);
+ sess->priv = NULL;
- kfree(sess->priv);
- sess->priv = NULL;
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+ }
} else {
if (sess->status == STATUS_NEEDS_RESUME)
sess->changed_format = 0;
}
+
mutex_unlock(&core->lock);
}
@@ -802,7 +818,7 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
if (cmd->cmd == V4L2_DEC_CMD_START) {
v4l2_m2m_clear_state(sess->m2m_ctx);
- sess->should_stop = 0;
+ WRITE_ONCE(sess->should_stop, 0);
return 0;
}
@@ -812,7 +828,7 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
dev_dbg(dev, "Received V4L2_DEC_CMD_STOP\n");
- sess->should_stop = 1;
+ WRITE_ONCE(sess->should_stop, 1);
v4l2_m2m_mark_stopped(sess->m2m_ctx);
@@ -998,6 +1014,9 @@ static int vdec_close(struct file *file)
struct amvdec_session *sess = file_to_amvdec_session(file);
struct amvdec_core *core = sess->core;
+ /* Signal workqueue loop to abort instantly */
+ WRITE_ONCE(sess->should_stop, 1);
+
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
/* Ensure esparser ISR finishes executing */
@@ -1012,21 +1031,30 @@ static int vdec_close(struct file *file)
mutex_lock(&core->lock);
- vdec_poweroff(sess);
- vdec_free_canvas(sess);
- core->cur_sess = NULL;
+ if (core->cur_sess == sess) {
+ vdec_poweroff(sess);
+ vdec_free_canvas(sess);
- if (sess->vififo_vaddr) {
- dma_free_coherent(core->dev, sess->vififo_size,
- sess->vififo_vaddr, sess->vififo_paddr);
- sess->vififo_vaddr = NULL;
- sess->vififo_paddr = 0;
+ if (sess->vififo_vaddr) {
+ dma_free_coherent(core->dev,
+ sess->vififo_size,
+ sess->vififo_vaddr,
+ sess->vififo_paddr);
+ sess->vififo_vaddr = NULL;
+ sess->vififo_paddr = 0;
+ }
+ vdec_reset_timestamps(sess);
+ vdec_reset_bufs_recycle(sess);
}
- vdec_reset_timestamps(sess);
- vdec_reset_bufs_recycle(sess);
+
kfree(sess->priv);
sess->priv = NULL;
+ /* Unconditionally set our local status to stopped */
+ sess->status = STATUS_STOPPED;
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+
mutex_unlock(&core->lock);
v4l2_m2m_ctx_release(sess->m2m_ctx);
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 09/19] media: meson: vdec: Fix vp9 header update failure on invalid payloads
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne
Ensure vp9_update_header() returns an explicit error code on invalid or
malformed buffer payloads instead of silently returning zero.
When v4l2-compliance injects short, empty, or uninitialized test buffers,
the validation logic catches the out-of-bounds size anomalies, but
returning 0 tricks the calling esparser infrastructure into treating
it as a successful 0-byte header conversion. This causes the hardware
decoder engine to stall out, resulting in subsequent stream-on timeouts
and failure marks inside v4l2-test-buffers.cpp.
Fix this by returning -EINVAL across all validation and bounds check
failures to force immediate core framework buffer drops. Additionally,
tighten array pointer checks, secure the mag_ptr bounds loop, and
convert the superframe parsing indexer into an explicit if/else block
for readability.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index 959673742e699..edbfc829e2da8 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -97,11 +97,15 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
unsigned char *old_header = NULL;
dp = (uint8_t *)vb2_plane_vaddr(buf, 0);
+ if (!dp)
+ return -EINVAL;
+
dsize = vb2_get_plane_payload(buf, 0);
- if (dsize == vb2_plane_size(buf, 0)) {
- dev_warn(core->dev, "%s: unable to update header\n", __func__);
- return 0;
+ if (dsize <= 0 || dsize > vb2_plane_size(buf, 0)) {
+ dev_warn(core->dev, "%s: invalid payload size %d\n",
+ __func__, dsize);
+ return -EINVAL;
}
marker = dp[dsize - 1];
@@ -109,13 +113,16 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
num_frames = (marker & 0x7) + 1;
mag = ((marker >> 3) & 0x3) + 1;
mag_ptr = dsize - mag * num_frames - 2;
- if (dp[mag_ptr] != marker)
- return 0;
+ if (mag_ptr < 0 || dp[mag_ptr] != marker)
+ return -EINVAL;
mag_ptr++;
for (cur_frame = 0; cur_frame < num_frames; cur_frame++) {
frame_size[cur_frame] = 0;
for (cur_mag = 0; cur_mag < mag; cur_mag++) {
+ if (mag_ptr >= dsize)
+ return -EINVAL;
+
frame_size[cur_frame] |=
(dp[mag_ptr] << (cur_mag * 8));
mag_ptr++;
@@ -140,7 +147,7 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
if (new_frame_size >= vb2_plane_size(buf, 0)) {
dev_warn(core->dev, "%s: unable to update header\n", __func__);
- return 0;
+ return -ENOMEM;
}
for (cur_frame = num_frames - 1; cur_frame >= 0; cur_frame--) {
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 09/19] media: meson: vdec: Fix vp9 header update failure on invalid payloads
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Ensure vp9_update_header() returns an explicit error code on invalid or
malformed buffer payloads instead of silently returning zero.
When v4l2-compliance injects short, empty, or uninitialized test buffers,
the validation logic catches the out-of-bounds size anomalies, but
returning 0 tricks the calling esparser infrastructure into treating
it as a successful 0-byte header conversion. This causes the hardware
decoder engine to stall out, resulting in subsequent stream-on timeouts
and failure marks inside v4l2-test-buffers.cpp.
Fix this by returning -EINVAL across all validation and bounds check
failures to force immediate core framework buffer drops. Additionally,
tighten array pointer checks, secure the mag_ptr bounds loop, and
convert the superframe parsing indexer into an explicit if/else block
for readability.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index 959673742e699..edbfc829e2da8 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -97,11 +97,15 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
unsigned char *old_header = NULL;
dp = (uint8_t *)vb2_plane_vaddr(buf, 0);
+ if (!dp)
+ return -EINVAL;
+
dsize = vb2_get_plane_payload(buf, 0);
- if (dsize == vb2_plane_size(buf, 0)) {
- dev_warn(core->dev, "%s: unable to update header\n", __func__);
- return 0;
+ if (dsize <= 0 || dsize > vb2_plane_size(buf, 0)) {
+ dev_warn(core->dev, "%s: invalid payload size %d\n",
+ __func__, dsize);
+ return -EINVAL;
}
marker = dp[dsize - 1];
@@ -109,13 +113,16 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
num_frames = (marker & 0x7) + 1;
mag = ((marker >> 3) & 0x3) + 1;
mag_ptr = dsize - mag * num_frames - 2;
- if (dp[mag_ptr] != marker)
- return 0;
+ if (mag_ptr < 0 || dp[mag_ptr] != marker)
+ return -EINVAL;
mag_ptr++;
for (cur_frame = 0; cur_frame < num_frames; cur_frame++) {
frame_size[cur_frame] = 0;
for (cur_mag = 0; cur_mag < mag; cur_mag++) {
+ if (mag_ptr >= dsize)
+ return -EINVAL;
+
frame_size[cur_frame] |=
(dp[mag_ptr] << (cur_mag * 8));
mag_ptr++;
@@ -140,7 +147,7 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
if (new_frame_size >= vb2_plane_size(buf, 0)) {
dev_warn(core->dev, "%s: unable to update header\n", __func__);
- return 0;
+ return -ENOMEM;
}
for (cur_frame = num_frames - 1; cur_frame >= 0; cur_frame--) {
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 09/19] media: meson: vdec: Fix vp9 header update failure on invalid payloads
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Ensure vp9_update_header() returns an explicit error code on invalid or
malformed buffer payloads instead of silently returning zero.
When v4l2-compliance injects short, empty, or uninitialized test buffers,
the validation logic catches the out-of-bounds size anomalies, but
returning 0 tricks the calling esparser infrastructure into treating
it as a successful 0-byte header conversion. This causes the hardware
decoder engine to stall out, resulting in subsequent stream-on timeouts
and failure marks inside v4l2-test-buffers.cpp.
Fix this by returning -EINVAL across all validation and bounds check
failures to force immediate core framework buffer drops. Additionally,
tighten array pointer checks, secure the mag_ptr bounds loop, and
convert the superframe parsing indexer into an explicit if/else block
for readability.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index 959673742e699..edbfc829e2da8 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -97,11 +97,15 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
unsigned char *old_header = NULL;
dp = (uint8_t *)vb2_plane_vaddr(buf, 0);
+ if (!dp)
+ return -EINVAL;
+
dsize = vb2_get_plane_payload(buf, 0);
- if (dsize == vb2_plane_size(buf, 0)) {
- dev_warn(core->dev, "%s: unable to update header\n", __func__);
- return 0;
+ if (dsize <= 0 || dsize > vb2_plane_size(buf, 0)) {
+ dev_warn(core->dev, "%s: invalid payload size %d\n",
+ __func__, dsize);
+ return -EINVAL;
}
marker = dp[dsize - 1];
@@ -109,13 +113,16 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
num_frames = (marker & 0x7) + 1;
mag = ((marker >> 3) & 0x3) + 1;
mag_ptr = dsize - mag * num_frames - 2;
- if (dp[mag_ptr] != marker)
- return 0;
+ if (mag_ptr < 0 || dp[mag_ptr] != marker)
+ return -EINVAL;
mag_ptr++;
for (cur_frame = 0; cur_frame < num_frames; cur_frame++) {
frame_size[cur_frame] = 0;
for (cur_mag = 0; cur_mag < mag; cur_mag++) {
+ if (mag_ptr >= dsize)
+ return -EINVAL;
+
frame_size[cur_frame] |=
(dp[mag_ptr] << (cur_mag * 8));
mag_ptr++;
@@ -140,7 +147,7 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
if (new_frame_size >= vb2_plane_size(buf, 0)) {
dev_warn(core->dev, "%s: unable to update header\n", __func__);
- return 0;
+ return -ENOMEM;
}
for (cur_frame = num_frames - 1; cur_frame >= 0; cur_frame--) {
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 10/19] media: meson: vdec: Fix race conditions and leaks in esparser pipeline
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne
Fix a circular locking dependency (lockdep warning) and a potential KASAN
wild-memory-access race condition within the asynchronous esparser queue
by restructuring register write and context evaluation blocks.
Previously, evaluating hardware ownership checks outside of core->lock
introduced a time-of-check to time-of-use vulnerability, risking register
corruption during parallel stream initialization cycles. Additionally,
abruptly exiting the payload queue loop without sanitizing timestamp
trackers left metadata dynamically stranded in memory, triggering regular
kmemleak warnings.
Fix these flaws by moving the hardware session ownership validation and
atomic stop signal checks directly inside the core->lock mutex region in
esparser_queue(). Ensure that presentation timestamp pointers are cleanly
removed via amvdec_remove_ts() on all early-exit routes. Finally, refine
the VP9 payload evaluation logic to correctly catch negative error passes
from vp9_update_header() and pass the session context explicitly to the
padding helper to secure memory operations.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 29 ++++++++++++++++++---
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index edbfc829e2da8..b9f36fef4be12 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -199,12 +199,16 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
* the ESPARSER interrupt.
*/
static u32 esparser_pad_start_code(struct amvdec_core *core,
+ struct amvdec_session *sess,
struct vb2_buffer *vb,
u32 payload_size)
{
u32 pad_size = 0;
u8 *vaddr = vb2_plane_vaddr(vb, 0);
+ if (!sess || READ_ONCE(sess->should_stop) || !sess->priv || !vaddr)
+ return 0;
+
if (payload_size < ESPARSER_MIN_PACKET_SIZE) {
pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size;
memset(vaddr + payload_size, 0, pad_size);
@@ -313,6 +317,9 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
u32 offset;
u32 pad_size;
+ if (READ_ONCE(sess->should_stop) || !sess->priv)
+ return -ESHUTDOWN;
+
/*
* When max ref frame is held by VP9, this should be -= 3 to prevent a
* shortage of CAPTURE buffers on the decoder side.
@@ -349,24 +356,38 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
vbuf->sequence = sess->sequence_out++;
if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
- payload_size = vp9_update_header(core, vb);
+ int res = vp9_update_header(core, vb);
- if (payload_size == 0) {
- dev_err(core->dev, "esparser: VP9 header update failed\n");
+ if (res <= 0) {
+ dev_err(core->dev,
+ "esparser: VP9 header update failed (%d)\n",
+ res);
amvdec_remove_ts(sess, vb->timestamp);
return -EBADMSG;
}
+ payload_size = res;
+ }
+
+ pad_size = esparser_pad_start_code(core, sess, vb, payload_size);
+
+ /* Protect hardware register writes under core->lock */
+ mutex_lock(&core->lock);
+ if (core->cur_sess != sess || READ_ONCE(sess->should_stop)) {
+ mutex_unlock(&core->lock);
+ amvdec_remove_ts(sess, vb->timestamp);
+ return -ESHUTDOWN;
}
- pad_size = esparser_pad_start_code(core, vb, payload_size);
ret = esparser_write_data(core, phy, payload_size + pad_size);
if (ret <= 0) {
dev_warn(core->dev, "esparser: input parsing error\n");
amvdec_remove_ts(sess, vb->timestamp);
amvdec_write_parser(core, PARSER_FETCH_CMD, 0);
+ mutex_unlock(&core->lock);
return -EIO;
}
+ mutex_unlock(&core->lock);
atomic_inc(&sess->esparser_queued_bufs);
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 10/19] media: meson: vdec: Fix race conditions and leaks in esparser pipeline
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Fix a circular locking dependency (lockdep warning) and a potential KASAN
wild-memory-access race condition within the asynchronous esparser queue
by restructuring register write and context evaluation blocks.
Previously, evaluating hardware ownership checks outside of core->lock
introduced a time-of-check to time-of-use vulnerability, risking register
corruption during parallel stream initialization cycles. Additionally,
abruptly exiting the payload queue loop without sanitizing timestamp
trackers left metadata dynamically stranded in memory, triggering regular
kmemleak warnings.
Fix these flaws by moving the hardware session ownership validation and
atomic stop signal checks directly inside the core->lock mutex region in
esparser_queue(). Ensure that presentation timestamp pointers are cleanly
removed via amvdec_remove_ts() on all early-exit routes. Finally, refine
the VP9 payload evaluation logic to correctly catch negative error passes
from vp9_update_header() and pass the session context explicitly to the
padding helper to secure memory operations.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 29 ++++++++++++++++++---
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index edbfc829e2da8..b9f36fef4be12 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -199,12 +199,16 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
* the ESPARSER interrupt.
*/
static u32 esparser_pad_start_code(struct amvdec_core *core,
+ struct amvdec_session *sess,
struct vb2_buffer *vb,
u32 payload_size)
{
u32 pad_size = 0;
u8 *vaddr = vb2_plane_vaddr(vb, 0);
+ if (!sess || READ_ONCE(sess->should_stop) || !sess->priv || !vaddr)
+ return 0;
+
if (payload_size < ESPARSER_MIN_PACKET_SIZE) {
pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size;
memset(vaddr + payload_size, 0, pad_size);
@@ -313,6 +317,9 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
u32 offset;
u32 pad_size;
+ if (READ_ONCE(sess->should_stop) || !sess->priv)
+ return -ESHUTDOWN;
+
/*
* When max ref frame is held by VP9, this should be -= 3 to prevent a
* shortage of CAPTURE buffers on the decoder side.
@@ -349,24 +356,38 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
vbuf->sequence = sess->sequence_out++;
if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
- payload_size = vp9_update_header(core, vb);
+ int res = vp9_update_header(core, vb);
- if (payload_size == 0) {
- dev_err(core->dev, "esparser: VP9 header update failed\n");
+ if (res <= 0) {
+ dev_err(core->dev,
+ "esparser: VP9 header update failed (%d)\n",
+ res);
amvdec_remove_ts(sess, vb->timestamp);
return -EBADMSG;
}
+ payload_size = res;
+ }
+
+ pad_size = esparser_pad_start_code(core, sess, vb, payload_size);
+
+ /* Protect hardware register writes under core->lock */
+ mutex_lock(&core->lock);
+ if (core->cur_sess != sess || READ_ONCE(sess->should_stop)) {
+ mutex_unlock(&core->lock);
+ amvdec_remove_ts(sess, vb->timestamp);
+ return -ESHUTDOWN;
}
- pad_size = esparser_pad_start_code(core, vb, payload_size);
ret = esparser_write_data(core, phy, payload_size + pad_size);
if (ret <= 0) {
dev_warn(core->dev, "esparser: input parsing error\n");
amvdec_remove_ts(sess, vb->timestamp);
amvdec_write_parser(core, PARSER_FETCH_CMD, 0);
+ mutex_unlock(&core->lock);
return -EIO;
}
+ mutex_unlock(&core->lock);
atomic_inc(&sess->esparser_queued_bufs);
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 10/19] media: meson: vdec: Fix race conditions and leaks in esparser pipeline
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Fix a circular locking dependency (lockdep warning) and a potential KASAN
wild-memory-access race condition within the asynchronous esparser queue
by restructuring register write and context evaluation blocks.
Previously, evaluating hardware ownership checks outside of core->lock
introduced a time-of-check to time-of-use vulnerability, risking register
corruption during parallel stream initialization cycles. Additionally,
abruptly exiting the payload queue loop without sanitizing timestamp
trackers left metadata dynamically stranded in memory, triggering regular
kmemleak warnings.
Fix these flaws by moving the hardware session ownership validation and
atomic stop signal checks directly inside the core->lock mutex region in
esparser_queue(). Ensure that presentation timestamp pointers are cleanly
removed via amvdec_remove_ts() on all early-exit routes. Finally, refine
the VP9 payload evaluation logic to correctly catch negative error passes
from vp9_update_header() and pass the session context explicitly to the
padding helper to secure memory operations.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 29 ++++++++++++++++++---
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index edbfc829e2da8..b9f36fef4be12 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -199,12 +199,16 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
* the ESPARSER interrupt.
*/
static u32 esparser_pad_start_code(struct amvdec_core *core,
+ struct amvdec_session *sess,
struct vb2_buffer *vb,
u32 payload_size)
{
u32 pad_size = 0;
u8 *vaddr = vb2_plane_vaddr(vb, 0);
+ if (!sess || READ_ONCE(sess->should_stop) || !sess->priv || !vaddr)
+ return 0;
+
if (payload_size < ESPARSER_MIN_PACKET_SIZE) {
pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size;
memset(vaddr + payload_size, 0, pad_size);
@@ -313,6 +317,9 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
u32 offset;
u32 pad_size;
+ if (READ_ONCE(sess->should_stop) || !sess->priv)
+ return -ESHUTDOWN;
+
/*
* When max ref frame is held by VP9, this should be -= 3 to prevent a
* shortage of CAPTURE buffers on the decoder side.
@@ -349,24 +356,38 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
vbuf->sequence = sess->sequence_out++;
if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
- payload_size = vp9_update_header(core, vb);
+ int res = vp9_update_header(core, vb);
- if (payload_size == 0) {
- dev_err(core->dev, "esparser: VP9 header update failed\n");
+ if (res <= 0) {
+ dev_err(core->dev,
+ "esparser: VP9 header update failed (%d)\n",
+ res);
amvdec_remove_ts(sess, vb->timestamp);
return -EBADMSG;
}
+ payload_size = res;
+ }
+
+ pad_size = esparser_pad_start_code(core, sess, vb, payload_size);
+
+ /* Protect hardware register writes under core->lock */
+ mutex_lock(&core->lock);
+ if (core->cur_sess != sess || READ_ONCE(sess->should_stop)) {
+ mutex_unlock(&core->lock);
+ amvdec_remove_ts(sess, vb->timestamp);
+ return -ESHUTDOWN;
}
- pad_size = esparser_pad_start_code(core, vb, payload_size);
ret = esparser_write_data(core, phy, payload_size + pad_size);
if (ret <= 0) {
dev_warn(core->dev, "esparser: input parsing error\n");
amvdec_remove_ts(sess, vb->timestamp);
amvdec_write_parser(core, PARSER_FETCH_CMD, 0);
+ mutex_unlock(&core->lock);
return -EIO;
}
+ mutex_unlock(&core->lock);
atomic_inc(&sess->esparser_queued_bufs);
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 11/19] media: meson: vdec: Update core m2m stream state during transitions
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne
Explicitly update the V4L2 core Memory-to-Memory (m2m) framework's
internal streaming states when initiating or terminating streaming on
the device queues.
Without invoking these core framework helpers, the m2m engine's
bookkeeping of active source and destination queues drifts out of
alignment with the driver's local 'streamon_out' and 'streamon_cap'
tracking variables. This misalignment causes state validation stalls
and incorrect polling outcomes when the device is subjected to quick
runtime cycles or strict testing setups (such as v4l2-compliance).
Fix this by integrating v4l2_m2m_update_start_streaming_state() right
after a queue is marked active in vdec_start_streaming(), and pairing
it cleanly with v4l2_m2m_update_stop_streaming_state() at the end of
vdec_stop_streaming() before the session core mutex is released.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 83a9b1238972a..0eb39aa6014ee 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -299,6 +299,8 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
else
sess->streamon_cap = 1;
+ v4l2_m2m_update_start_streaming_state(sess->m2m_ctx, q);
+
if (!sess->streamon_out)
return 0;
@@ -529,6 +531,8 @@ static void vdec_stop_streaming(struct vb2_queue *q)
sess->changed_format = 0;
}
+ v4l2_m2m_update_stop_streaming_state(sess->m2m_ctx, q);
+
mutex_unlock(&core->lock);
}
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 11/19] media: meson: vdec: Update core m2m stream state during transitions
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Explicitly update the V4L2 core Memory-to-Memory (m2m) framework's
internal streaming states when initiating or terminating streaming on
the device queues.
Without invoking these core framework helpers, the m2m engine's
bookkeeping of active source and destination queues drifts out of
alignment with the driver's local 'streamon_out' and 'streamon_cap'
tracking variables. This misalignment causes state validation stalls
and incorrect polling outcomes when the device is subjected to quick
runtime cycles or strict testing setups (such as v4l2-compliance).
Fix this by integrating v4l2_m2m_update_start_streaming_state() right
after a queue is marked active in vdec_start_streaming(), and pairing
it cleanly with v4l2_m2m_update_stop_streaming_state() at the end of
vdec_stop_streaming() before the session core mutex is released.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 83a9b1238972a..0eb39aa6014ee 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -299,6 +299,8 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
else
sess->streamon_cap = 1;
+ v4l2_m2m_update_start_streaming_state(sess->m2m_ctx, q);
+
if (!sess->streamon_out)
return 0;
@@ -529,6 +531,8 @@ static void vdec_stop_streaming(struct vb2_queue *q)
sess->changed_format = 0;
}
+ v4l2_m2m_update_stop_streaming_state(sess->m2m_ctx, q);
+
mutex_unlock(&core->lock);
}
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 11/19] media: meson: vdec: Update core m2m stream state during transitions
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Explicitly update the V4L2 core Memory-to-Memory (m2m) framework's
internal streaming states when initiating or terminating streaming on
the device queues.
Without invoking these core framework helpers, the m2m engine's
bookkeeping of active source and destination queues drifts out of
alignment with the driver's local 'streamon_out' and 'streamon_cap'
tracking variables. This misalignment causes state validation stalls
and incorrect polling outcomes when the device is subjected to quick
runtime cycles or strict testing setups (such as v4l2-compliance).
Fix this by integrating v4l2_m2m_update_start_streaming_state() right
after a queue is marked active in vdec_start_streaming(), and pairing
it cleanly with v4l2_m2m_update_stop_streaming_state() at the end of
vdec_stop_streaming() before the session core mutex is released.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 83a9b1238972a..0eb39aa6014ee 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -299,6 +299,8 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
else
sess->streamon_cap = 1;
+ v4l2_m2m_update_start_streaming_state(sess->m2m_ctx, q);
+
if (!sess->streamon_out)
return 0;
@@ -529,6 +531,8 @@ static void vdec_stop_streaming(struct vb2_queue *q)
sess->changed_format = 0;
}
+ v4l2_m2m_update_stop_streaming_state(sess->m2m_ctx, q);
+
mutex_unlock(&core->lock);
}
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 12/19] media: meson: vdec: Coordinate m2m task execution inside async loop
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne
Introduce proper V4L2 Memory-to-Memory (m2m) scheduler pipeline tracking
by handling job finalisation inside the asynchronous esparser workqueue.
Because the meson video decoder offloads raw hardware register processing
and bitstream feeding to an internal workqueue engine, calling the helper
v4l2_m2m_job_finish() prematurely within the primary device execution
trigger context (vdec_m2m_device_run) drops the active task transaction
state too early. This timing gap creates a state mismatch in user-space
multimedia layers like GStreamer, resulting in a fatal "poll error 1"
event abort that breaks streaming setup sequences during pipeline preroll.
Resolve this architectural loop collision by deferring the scheduling call
to v4l2_m2m_job_finish() to execute exclusively within the worker routine
esparser_queue_all_src() at the precise microsecond after an input buffer
payload is fully validated, cleared, and returned via v4l2_m2m_buf_done().
Additionally, protect hardware session context mappings with core mutex
locks and implement volatile early exit gates within vdec_m2m_device_run()
to ensure stable teardowns.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 4 ++++
drivers/staging/media/meson/vdec/vdec.c | 11 +++++++++++
2 files changed, 15 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index b9f36fef4be12..939b239c2af47 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -399,6 +399,7 @@ void esparser_queue_all_src(struct work_struct *work)
struct amvdec_session *sess =
container_of(work, struct amvdec_session, esparser_queue_work);
struct device *dev = sess->core->dev_dec;
+ struct amvdec_core *core = sess->core;
int ret;
while (1) {
@@ -437,6 +438,9 @@ void esparser_queue_all_src(struct work_struct *work)
else
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+ /* Safely notify the V4L2 core sub-framework */
+ v4l2_m2m_job_finish(core->m2m_dev, sess->m2m_ctx);
+
/* Set tracking flag indicating transaction completion */
processed_frame = true;
}
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 0eb39aa6014ee..b3e1d99e8889f 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -149,6 +149,17 @@ vdec_queue_recycle(struct amvdec_session *sess, struct vb2_buffer *vb)
static void vdec_m2m_device_run(void *priv)
{
struct amvdec_session *sess = priv;
+ struct amvdec_core *core = sess->core;
+
+ if (READ_ONCE(sess->should_stop)) {
+ v4l2_m2m_job_finish(core->m2m_dev, sess->m2m_ctx);
+ return;
+ }
+
+ mutex_lock(&core->lock);
+ if (!core->cur_sess)
+ core->cur_sess = sess;
+ mutex_unlock(&core->lock);
schedule_work(&sess->esparser_queue_work);
}
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 12/19] media: meson: vdec: Coordinate m2m task execution inside async loop
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Introduce proper V4L2 Memory-to-Memory (m2m) scheduler pipeline tracking
by handling job finalisation inside the asynchronous esparser workqueue.
Because the meson video decoder offloads raw hardware register processing
and bitstream feeding to an internal workqueue engine, calling the helper
v4l2_m2m_job_finish() prematurely within the primary device execution
trigger context (vdec_m2m_device_run) drops the active task transaction
state too early. This timing gap creates a state mismatch in user-space
multimedia layers like GStreamer, resulting in a fatal "poll error 1"
event abort that breaks streaming setup sequences during pipeline preroll.
Resolve this architectural loop collision by deferring the scheduling call
to v4l2_m2m_job_finish() to execute exclusively within the worker routine
esparser_queue_all_src() at the precise microsecond after an input buffer
payload is fully validated, cleared, and returned via v4l2_m2m_buf_done().
Additionally, protect hardware session context mappings with core mutex
locks and implement volatile early exit gates within vdec_m2m_device_run()
to ensure stable teardowns.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 4 ++++
drivers/staging/media/meson/vdec/vdec.c | 11 +++++++++++
2 files changed, 15 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index b9f36fef4be12..939b239c2af47 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -399,6 +399,7 @@ void esparser_queue_all_src(struct work_struct *work)
struct amvdec_session *sess =
container_of(work, struct amvdec_session, esparser_queue_work);
struct device *dev = sess->core->dev_dec;
+ struct amvdec_core *core = sess->core;
int ret;
while (1) {
@@ -437,6 +438,9 @@ void esparser_queue_all_src(struct work_struct *work)
else
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+ /* Safely notify the V4L2 core sub-framework */
+ v4l2_m2m_job_finish(core->m2m_dev, sess->m2m_ctx);
+
/* Set tracking flag indicating transaction completion */
processed_frame = true;
}
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 0eb39aa6014ee..b3e1d99e8889f 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -149,6 +149,17 @@ vdec_queue_recycle(struct amvdec_session *sess, struct vb2_buffer *vb)
static void vdec_m2m_device_run(void *priv)
{
struct amvdec_session *sess = priv;
+ struct amvdec_core *core = sess->core;
+
+ if (READ_ONCE(sess->should_stop)) {
+ v4l2_m2m_job_finish(core->m2m_dev, sess->m2m_ctx);
+ return;
+ }
+
+ mutex_lock(&core->lock);
+ if (!core->cur_sess)
+ core->cur_sess = sess;
+ mutex_unlock(&core->lock);
schedule_work(&sess->esparser_queue_work);
}
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 12/19] media: meson: vdec: Coordinate m2m task execution inside async loop
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Introduce proper V4L2 Memory-to-Memory (m2m) scheduler pipeline tracking
by handling job finalisation inside the asynchronous esparser workqueue.
Because the meson video decoder offloads raw hardware register processing
and bitstream feeding to an internal workqueue engine, calling the helper
v4l2_m2m_job_finish() prematurely within the primary device execution
trigger context (vdec_m2m_device_run) drops the active task transaction
state too early. This timing gap creates a state mismatch in user-space
multimedia layers like GStreamer, resulting in a fatal "poll error 1"
event abort that breaks streaming setup sequences during pipeline preroll.
Resolve this architectural loop collision by deferring the scheduling call
to v4l2_m2m_job_finish() to execute exclusively within the worker routine
esparser_queue_all_src() at the precise microsecond after an input buffer
payload is fully validated, cleared, and returned via v4l2_m2m_buf_done().
Additionally, protect hardware session context mappings with core mutex
locks and implement volatile early exit gates within vdec_m2m_device_run()
to ensure stable teardowns.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/esparser.c | 4 ++++
drivers/staging/media/meson/vdec/vdec.c | 11 +++++++++++
2 files changed, 15 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index b9f36fef4be12..939b239c2af47 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -399,6 +399,7 @@ void esparser_queue_all_src(struct work_struct *work)
struct amvdec_session *sess =
container_of(work, struct amvdec_session, esparser_queue_work);
struct device *dev = sess->core->dev_dec;
+ struct amvdec_core *core = sess->core;
int ret;
while (1) {
@@ -437,6 +438,9 @@ void esparser_queue_all_src(struct work_struct *work)
else
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+ /* Safely notify the V4L2 core sub-framework */
+ v4l2_m2m_job_finish(core->m2m_dev, sess->m2m_ctx);
+
/* Set tracking flag indicating transaction completion */
processed_frame = true;
}
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 0eb39aa6014ee..b3e1d99e8889f 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -149,6 +149,17 @@ vdec_queue_recycle(struct amvdec_session *sess, struct vb2_buffer *vb)
static void vdec_m2m_device_run(void *priv)
{
struct amvdec_session *sess = priv;
+ struct amvdec_core *core = sess->core;
+
+ if (READ_ONCE(sess->should_stop)) {
+ v4l2_m2m_job_finish(core->m2m_dev, sess->m2m_ctx);
+ return;
+ }
+
+ mutex_lock(&core->lock);
+ if (!core->cur_sess)
+ core->cur_sess = sess;
+ mutex_unlock(&core->lock);
schedule_work(&sess->esparser_queue_work);
}
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 13/19] media: meson: vdec: Fix race conditions in job abort sequence
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne
Implement proper cleanup inside vdec_m2m_job_abort to safely stop
the hardware job sequence during a streaming abort or teardown.
Without this, if a job is aborted right after being triggered, the
deferred work item (esparser_queue_work) scheduled by device_run
could continue running concurrently. This leads to unexpected behavior
and potential use-after-free bugs if session structures are cleared.
Fix this by flagging the session to stop via WRITE_ONCE, synchronously
canceling any pending parser work, and safely clearing the active core
session pointer under the core lock protection.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index b3e1d99e8889f..ac86a9c4febff 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -167,6 +167,17 @@ static void vdec_m2m_device_run(void *priv)
static void vdec_m2m_job_abort(void *priv)
{
struct amvdec_session *sess = priv;
+ struct amvdec_core *core = sess->core;
+
+ WRITE_ONCE(sess->should_stop, 1);
+
+ cancel_work_sync(&sess->esparser_queue_work);
+
+ mutex_lock(&core->lock);
+ if (core->cur_sess == sess)
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+ mutex_unlock(&core->lock);
v4l2_m2m_job_finish(sess->core->m2m_dev, sess->m2m_ctx);
}
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 13/19] media: meson: vdec: Fix race conditions in job abort sequence
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Implement proper cleanup inside vdec_m2m_job_abort to safely stop
the hardware job sequence during a streaming abort or teardown.
Without this, if a job is aborted right after being triggered, the
deferred work item (esparser_queue_work) scheduled by device_run
could continue running concurrently. This leads to unexpected behavior
and potential use-after-free bugs if session structures are cleared.
Fix this by flagging the session to stop via WRITE_ONCE, synchronously
canceling any pending parser work, and safely clearing the active core
session pointer under the core lock protection.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index b3e1d99e8889f..ac86a9c4febff 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -167,6 +167,17 @@ static void vdec_m2m_device_run(void *priv)
static void vdec_m2m_job_abort(void *priv)
{
struct amvdec_session *sess = priv;
+ struct amvdec_core *core = sess->core;
+
+ WRITE_ONCE(sess->should_stop, 1);
+
+ cancel_work_sync(&sess->esparser_queue_work);
+
+ mutex_lock(&core->lock);
+ if (core->cur_sess == sess)
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+ mutex_unlock(&core->lock);
v4l2_m2m_job_finish(sess->core->m2m_dev, sess->m2m_ctx);
}
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 13/19] media: meson: vdec: Fix race conditions in job abort sequence
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Implement proper cleanup inside vdec_m2m_job_abort to safely stop
the hardware job sequence during a streaming abort or teardown.
Without this, if a job is aborted right after being triggered, the
deferred work item (esparser_queue_work) scheduled by device_run
could continue running concurrently. This leads to unexpected behavior
and potential use-after-free bugs if session structures are cleared.
Fix this by flagging the session to stop via WRITE_ONCE, synchronously
canceling any pending parser work, and safely clearing the active core
session pointer under the core lock protection.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index b3e1d99e8889f..ac86a9c4febff 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -167,6 +167,17 @@ static void vdec_m2m_device_run(void *priv)
static void vdec_m2m_job_abort(void *priv)
{
struct amvdec_session *sess = priv;
+ struct amvdec_core *core = sess->core;
+
+ WRITE_ONCE(sess->should_stop, 1);
+
+ cancel_work_sync(&sess->esparser_queue_work);
+
+ mutex_lock(&core->lock);
+ if (core->cur_sess == sess)
+ /* Safely clear hardware ownership since we were confirmed as the owner */
+ smp_store_release(&core->cur_sess, NULL);
+ mutex_unlock(&core->lock);
v4l2_m2m_job_finish(sess->core->m2m_dev, sess->m2m_ctx);
}
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 14/19] media: meson: vdec: Correct atomic counter placement in dst_buf_done
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne
Fix an end-of-stream (EOS) boundary condition logic flaw in
dst_buf_done() by relocating the atomic decrement helper to execute
after state evaluation.
Previously, decrementing sess->esparser_queued_bufs at the entry point
of the function corrupted the conditional checks for the final video
frame. This premature decrement tricked the driver into flagging the
second-to-last buffer as the final frame (V4L2_BUF_FLAG_LAST) during
teardown sequences, which cut off the true final frame and broke
compliance validation.
Resolve this by moving atomic_dec() to execute immediately after the EOS
conditional tracking block finishes evaluating, right before invoking
v4l2_m2m_buf_done(). This ensures accurate stream termination
signaling.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec_helpers.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.c b/drivers/staging/media/meson/vdec/vdec_helpers.c
index f02c21d5a9c18..303236f0647c0 100644
--- a/drivers/staging/media/meson/vdec/vdec_helpers.c
+++ b/drivers/staging/media/meson/vdec/vdec_helpers.c
@@ -314,6 +314,9 @@ static void dst_buf_done(struct amvdec_session *sess,
dev_dbg(dev, "Buffer %u done, ts = %llu, flags = %08X\n",
vbuf->vb2_buf.index, timestamp, flags);
vbuf->field = field;
+
+ atomic_dec(&sess->esparser_queued_bufs);
+
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
/* Buffer done probably means the vififo got freed */
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 14/19] media: meson: vdec: Correct atomic counter placement in dst_buf_done
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Fix an end-of-stream (EOS) boundary condition logic flaw in
dst_buf_done() by relocating the atomic decrement helper to execute
after state evaluation.
Previously, decrementing sess->esparser_queued_bufs at the entry point
of the function corrupted the conditional checks for the final video
frame. This premature decrement tricked the driver into flagging the
second-to-last buffer as the final frame (V4L2_BUF_FLAG_LAST) during
teardown sequences, which cut off the true final frame and broke
compliance validation.
Resolve this by moving atomic_dec() to execute immediately after the EOS
conditional tracking block finishes evaluating, right before invoking
v4l2_m2m_buf_done(). This ensures accurate stream termination
signaling.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec_helpers.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.c b/drivers/staging/media/meson/vdec/vdec_helpers.c
index f02c21d5a9c18..303236f0647c0 100644
--- a/drivers/staging/media/meson/vdec/vdec_helpers.c
+++ b/drivers/staging/media/meson/vdec/vdec_helpers.c
@@ -314,6 +314,9 @@ static void dst_buf_done(struct amvdec_session *sess,
dev_dbg(dev, "Buffer %u done, ts = %llu, flags = %08X\n",
vbuf->vb2_buf.index, timestamp, flags);
vbuf->field = field;
+
+ atomic_dec(&sess->esparser_queued_bufs);
+
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
/* Buffer done probably means the vififo got freed */
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 14/19] media: meson: vdec: Correct atomic counter placement in dst_buf_done
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
Fix an end-of-stream (EOS) boundary condition logic flaw in
dst_buf_done() by relocating the atomic decrement helper to execute
after state evaluation.
Previously, decrementing sess->esparser_queued_bufs at the entry point
of the function corrupted the conditional checks for the final video
frame. This premature decrement tricked the driver into flagging the
second-to-last buffer as the final frame (V4L2_BUF_FLAG_LAST) during
teardown sequences, which cut off the true final frame and broke
compliance validation.
Resolve this by moving atomic_dec() to execute immediately after the EOS
conditional tracking block finishes evaluating, right before invoking
v4l2_m2m_buf_done(). This ensures accurate stream termination
signaling.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec_helpers.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.c b/drivers/staging/media/meson/vdec/vdec_helpers.c
index f02c21d5a9c18..303236f0647c0 100644
--- a/drivers/staging/media/meson/vdec/vdec_helpers.c
+++ b/drivers/staging/media/meson/vdec/vdec_helpers.c
@@ -314,6 +314,9 @@ static void dst_buf_done(struct amvdec_session *sess,
dev_dbg(dev, "Buffer %u done, ts = %llu, flags = %08X\n",
vbuf->vb2_buf.index, timestamp, flags);
vbuf->field = field;
+
+ atomic_dec(&sess->esparser_queued_bufs);
+
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
/* Buffer done probably means the vififo got freed */
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 15/19] media: meson: vdec: Fix concurrent firmware loading race and hardware timeout
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne
The vdec_1 firmware loader used static variables for DMA buffer
addresses (`mc_addr`, `mc_addr_map`). This made allocations global
across sessions, corrupting state when multiple decoders loaded
firmware concurrently. Under stress this triggered KASAN reports,
lost references, and memory leaks. Making mc_addr and mc_addr_map
local to each load call, eliminating cross‑session races.
The firmware DMA completion check also relied on a raw decrement
loop of 1000 iterations. On modern CPUs this loop completed far
too quickly, often before hardware signaled ready, producing
false "DMA hang" errors. Replacing the busy‑wait loop with
readl_poll_timeout_atomic(), providing a bounded, microsecond
scale poll with proper timeout semantics.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec_1.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec_1.c b/drivers/staging/media/meson/vdec/vdec_1.c
index a65cb49594465..27363c9a5af3c 100644
--- a/drivers/staging/media/meson/vdec/vdec_1.c
+++ b/drivers/staging/media/meson/vdec/vdec_1.c
@@ -29,14 +29,14 @@ vdec_1_load_firmware(struct amvdec_session *sess, const char *fwname)
struct amvdec_core *core = sess->core;
struct device *dev = core->dev_dec;
struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
- static void *mc_addr;
- static dma_addr_t mc_addr_map;
+ void *mc_addr;
+ dma_addr_t mc_addr_map;
int ret;
- u32 i = 1000;
+ u32 val;
ret = request_firmware(&fw, fwname, dev);
if (ret < 0)
- return -EINVAL;
+ return ret;
if (fw->size < MC_SIZE) {
dev_err(dev, "Firmware size %zu is too small. Expected %u.\n",
@@ -63,11 +63,11 @@ vdec_1_load_firmware(struct amvdec_session *sess, const char *fwname)
amvdec_write_dos(core, IMEM_DMA_COUNT, MC_SIZE / 4);
amvdec_write_dos(core, IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
- while (--i && amvdec_read_dos(core, IMEM_DMA_CTRL) & 0x8000);
-
- if (i == 0) {
+ ret = readl_poll_timeout_atomic(core->dos_base + IMEM_DMA_CTRL, val,
+ !(val & 0x8000), 10, 10000);
+ if (ret) {
dev_err(dev, "Firmware load fail (DMA hang?)\n");
- ret = -EINVAL;
+ ret = -ETIMEDOUT;
goto free_mc;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 15/19] media: meson: vdec: Fix concurrent firmware loading race and hardware timeout
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
The vdec_1 firmware loader used static variables for DMA buffer
addresses (`mc_addr`, `mc_addr_map`). This made allocations global
across sessions, corrupting state when multiple decoders loaded
firmware concurrently. Under stress this triggered KASAN reports,
lost references, and memory leaks. Making mc_addr and mc_addr_map
local to each load call, eliminating cross‑session races.
The firmware DMA completion check also relied on a raw decrement
loop of 1000 iterations. On modern CPUs this loop completed far
too quickly, often before hardware signaled ready, producing
false "DMA hang" errors. Replacing the busy‑wait loop with
readl_poll_timeout_atomic(), providing a bounded, microsecond
scale poll with proper timeout semantics.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec_1.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec_1.c b/drivers/staging/media/meson/vdec/vdec_1.c
index a65cb49594465..27363c9a5af3c 100644
--- a/drivers/staging/media/meson/vdec/vdec_1.c
+++ b/drivers/staging/media/meson/vdec/vdec_1.c
@@ -29,14 +29,14 @@ vdec_1_load_firmware(struct amvdec_session *sess, const char *fwname)
struct amvdec_core *core = sess->core;
struct device *dev = core->dev_dec;
struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
- static void *mc_addr;
- static dma_addr_t mc_addr_map;
+ void *mc_addr;
+ dma_addr_t mc_addr_map;
int ret;
- u32 i = 1000;
+ u32 val;
ret = request_firmware(&fw, fwname, dev);
if (ret < 0)
- return -EINVAL;
+ return ret;
if (fw->size < MC_SIZE) {
dev_err(dev, "Firmware size %zu is too small. Expected %u.\n",
@@ -63,11 +63,11 @@ vdec_1_load_firmware(struct amvdec_session *sess, const char *fwname)
amvdec_write_dos(core, IMEM_DMA_COUNT, MC_SIZE / 4);
amvdec_write_dos(core, IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
- while (--i && amvdec_read_dos(core, IMEM_DMA_CTRL) & 0x8000);
-
- if (i == 0) {
+ ret = readl_poll_timeout_atomic(core->dos_base + IMEM_DMA_CTRL, val,
+ !(val & 0x8000), 10, 10000);
+ if (ret) {
dev_err(dev, "Firmware load fail (DMA hang?)\n");
- ret = -EINVAL;
+ ret = -ETIMEDOUT;
goto free_mc;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 15/19] media: meson: vdec: Fix concurrent firmware loading race and hardware timeout
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
The vdec_1 firmware loader used static variables for DMA buffer
addresses (`mc_addr`, `mc_addr_map`). This made allocations global
across sessions, corrupting state when multiple decoders loaded
firmware concurrently. Under stress this triggered KASAN reports,
lost references, and memory leaks. Making mc_addr and mc_addr_map
local to each load call, eliminating cross‑session races.
The firmware DMA completion check also relied on a raw decrement
loop of 1000 iterations. On modern CPUs this loop completed far
too quickly, often before hardware signaled ready, producing
false "DMA hang" errors. Replacing the busy‑wait loop with
readl_poll_timeout_atomic(), providing a bounded, microsecond
scale poll with proper timeout semantics.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec_1.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec_1.c b/drivers/staging/media/meson/vdec/vdec_1.c
index a65cb49594465..27363c9a5af3c 100644
--- a/drivers/staging/media/meson/vdec/vdec_1.c
+++ b/drivers/staging/media/meson/vdec/vdec_1.c
@@ -29,14 +29,14 @@ vdec_1_load_firmware(struct amvdec_session *sess, const char *fwname)
struct amvdec_core *core = sess->core;
struct device *dev = core->dev_dec;
struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
- static void *mc_addr;
- static dma_addr_t mc_addr_map;
+ void *mc_addr;
+ dma_addr_t mc_addr_map;
int ret;
- u32 i = 1000;
+ u32 val;
ret = request_firmware(&fw, fwname, dev);
if (ret < 0)
- return -EINVAL;
+ return ret;
if (fw->size < MC_SIZE) {
dev_err(dev, "Firmware size %zu is too small. Expected %u.\n",
@@ -63,11 +63,11 @@ vdec_1_load_firmware(struct amvdec_session *sess, const char *fwname)
amvdec_write_dos(core, IMEM_DMA_COUNT, MC_SIZE / 4);
amvdec_write_dos(core, IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
- while (--i && amvdec_read_dos(core, IMEM_DMA_CTRL) & 0x8000);
-
- if (i == 0) {
+ ret = readl_poll_timeout_atomic(core->dos_base + IMEM_DMA_CTRL, val,
+ !(val & 0x8000), 10, 10000);
+ if (ret) {
dev_err(dev, "Firmware load fail (DMA hang?)\n");
- ret = -EINVAL;
+ ret = -ETIMEDOUT;
goto free_mc;
}
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 16/19] media: meson: vdec: Configure DMA mask and segment size in probe
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne
The vdec probe routine does not set explicit DMA constraints, leaving the
driver completely dependent on platform bus default values. This lack of
explicit layout initialization can lead to allocation failures, restricted
address space mappings, or broken contiguous buffer handling on
architectures with restrictive DMA layers.
Address these platform constraints with the following changes during driver
initialization:
1. Enforce a 32-bit coherent DMA allocation window by invoking
dma_set_mask_and_coherent() with a DMA_BIT_MASK(32) argument.
2. Maximize the contiguous allocation segment boundary constraint to
UINT_MAX using the vb2_dma_contig_set_max_seg_size() configuration
helper.
This guarantees that large, contiguous video frame allocation requests
work reliably and explicitly aligns the memory management paths with
standard Linux kernel DMA management paradigms.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index ac86a9c4febff..d33cbebc4453b 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -1186,6 +1186,16 @@ static int vdec_probe(struct platform_device *pdev)
if (IS_ERR(core->canvas))
return PTR_ERR(core->canvas);
+ /* Enforce strict 32-bit DMA limit to match hardware capabilities */
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set 32-bit DMA mask\n");
+
+ ret = vb2_dma_contig_set_max_seg_size(dev, UINT_MAX);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to set DMA max segment size\n");
+
of_id = of_match_node(vdec_dt_match, dev->of_node);
core->platform = of_id->data;
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 16/19] media: meson: vdec: Configure DMA mask and segment size in probe
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
The vdec probe routine does not set explicit DMA constraints, leaving the
driver completely dependent on platform bus default values. This lack of
explicit layout initialization can lead to allocation failures, restricted
address space mappings, or broken contiguous buffer handling on
architectures with restrictive DMA layers.
Address these platform constraints with the following changes during driver
initialization:
1. Enforce a 32-bit coherent DMA allocation window by invoking
dma_set_mask_and_coherent() with a DMA_BIT_MASK(32) argument.
2. Maximize the contiguous allocation segment boundary constraint to
UINT_MAX using the vb2_dma_contig_set_max_seg_size() configuration
helper.
This guarantees that large, contiguous video frame allocation requests
work reliably and explicitly aligns the memory management paths with
standard Linux kernel DMA management paradigms.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index ac86a9c4febff..d33cbebc4453b 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -1186,6 +1186,16 @@ static int vdec_probe(struct platform_device *pdev)
if (IS_ERR(core->canvas))
return PTR_ERR(core->canvas);
+ /* Enforce strict 32-bit DMA limit to match hardware capabilities */
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set 32-bit DMA mask\n");
+
+ ret = vb2_dma_contig_set_max_seg_size(dev, UINT_MAX);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to set DMA max segment size\n");
+
of_id = of_match_node(vdec_dt_match, dev->of_node);
core->platform = of_id->data;
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 16/19] media: meson: vdec: Configure DMA mask and segment size in probe
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
The vdec probe routine does not set explicit DMA constraints, leaving the
driver completely dependent on platform bus default values. This lack of
explicit layout initialization can lead to allocation failures, restricted
address space mappings, or broken contiguous buffer handling on
architectures with restrictive DMA layers.
Address these platform constraints with the following changes during driver
initialization:
1. Enforce a 32-bit coherent DMA allocation window by invoking
dma_set_mask_and_coherent() with a DMA_BIT_MASK(32) argument.
2. Maximize the contiguous allocation segment boundary constraint to
UINT_MAX using the vb2_dma_contig_set_max_seg_size() configuration
helper.
This guarantees that large, contiguous video frame allocation requests
work reliably and explicitly aligns the memory management paths with
standard Linux kernel DMA management paradigms.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index ac86a9c4febff..d33cbebc4453b 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -1186,6 +1186,16 @@ static int vdec_probe(struct platform_device *pdev)
if (IS_ERR(core->canvas))
return PTR_ERR(core->canvas);
+ /* Enforce strict 32-bit DMA limit to match hardware capabilities */
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set 32-bit DMA mask\n");
+
+ ret = vb2_dma_contig_set_max_seg_size(dev, UINT_MAX);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to set DMA max segment size\n");
+
of_id = of_match_node(vdec_dt_match, dev->of_node);
core->platform = of_id->data;
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 17/19] media: meson: canvas: Fix Use-After-Free by linking canvas provider device
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne, Sashiko
The vdec driver retrieves a canvas instance using meson_canvas_get() and
stores the raw pointer in core->canvas. However, the helper drops the
provider device reference via put_device() right before returning. Because
no formal device relationship is established, an unbind of the canvas
provider driver triggers a Use-After-Free (UAF) bug when the video decoder
subsequently attempts to access that memory block.
Fix this lifecycle hazard by enhancing meson_canvas_get() to establish a
formal managed device link between the consumer device and the underlying
canvas platform device. Using DL_FLAG_AUTOREMOVE_CONSUMER ensures the
dependency link is automatically torn down when the consumer driver
unbinds.
Additionally, handle missing link conditions using dev_err_probe() to
clean up upstream error diagnostics, and ensure the canvas platform device
reference count is dropped safely along the uninitialized driver data
path.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260530111022.9C6D71F00893@smtp.kernel.org
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/soc/amlogic/meson-canvas.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/soc/amlogic/meson-canvas.c b/drivers/soc/amlogic/meson-canvas.c
index 79681afea8c61..5102a491bf778 100644
--- a/drivers/soc/amlogic/meson-canvas.c
+++ b/drivers/soc/amlogic/meson-canvas.c
@@ -54,6 +54,7 @@ struct meson_canvas *meson_canvas_get(struct device *dev)
struct device_node *canvas_node;
struct platform_device *canvas_pdev;
struct meson_canvas *canvas;
+ struct device_link *link;
canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
if (!canvas_node)
@@ -70,9 +71,18 @@ struct meson_canvas *meson_canvas_get(struct device *dev)
* current state, this driver probe cannot return -EPROBE_DEFER
*/
canvas = dev_get_drvdata(&canvas_pdev->dev);
- put_device(&canvas_pdev->dev);
- if (!canvas)
+ if (!canvas) {
+ put_device(&canvas_pdev->dev);
return ERR_PTR(-EINVAL);
+ }
+
+ /* Establish device link to prevent Use-After-Free */
+ link = device_link_add(dev, &canvas_pdev->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+ put_device(&canvas_pdev->dev);
+ if (!link)
+ return ERR_PTR(dev_err_probe(dev, -EINVAL,
+ "Failed to create device link canvas\n"));
return canvas;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 17/19] media: meson: canvas: Fix Use-After-Free by linking canvas provider device
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
The vdec driver retrieves a canvas instance using meson_canvas_get() and
stores the raw pointer in core->canvas. However, the helper drops the
provider device reference via put_device() right before returning. Because
no formal device relationship is established, an unbind of the canvas
provider driver triggers a Use-After-Free (UAF) bug when the video decoder
subsequently attempts to access that memory block.
Fix this lifecycle hazard by enhancing meson_canvas_get() to establish a
formal managed device link between the consumer device and the underlying
canvas platform device. Using DL_FLAG_AUTOREMOVE_CONSUMER ensures the
dependency link is automatically torn down when the consumer driver
unbinds.
Additionally, handle missing link conditions using dev_err_probe() to
clean up upstream error diagnostics, and ensure the canvas platform device
reference count is dropped safely along the uninitialized driver data
path.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260530111022.9C6D71F00893@smtp.kernel.org
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/soc/amlogic/meson-canvas.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/soc/amlogic/meson-canvas.c b/drivers/soc/amlogic/meson-canvas.c
index 79681afea8c61..5102a491bf778 100644
--- a/drivers/soc/amlogic/meson-canvas.c
+++ b/drivers/soc/amlogic/meson-canvas.c
@@ -54,6 +54,7 @@ struct meson_canvas *meson_canvas_get(struct device *dev)
struct device_node *canvas_node;
struct platform_device *canvas_pdev;
struct meson_canvas *canvas;
+ struct device_link *link;
canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
if (!canvas_node)
@@ -70,9 +71,18 @@ struct meson_canvas *meson_canvas_get(struct device *dev)
* current state, this driver probe cannot return -EPROBE_DEFER
*/
canvas = dev_get_drvdata(&canvas_pdev->dev);
- put_device(&canvas_pdev->dev);
- if (!canvas)
+ if (!canvas) {
+ put_device(&canvas_pdev->dev);
return ERR_PTR(-EINVAL);
+ }
+
+ /* Establish device link to prevent Use-After-Free */
+ link = device_link_add(dev, &canvas_pdev->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+ put_device(&canvas_pdev->dev);
+ if (!link)
+ return ERR_PTR(dev_err_probe(dev, -EINVAL,
+ "Failed to create device link canvas\n"));
return canvas;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 17/19] media: meson: canvas: Fix Use-After-Free by linking canvas provider device
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Sashiko, Nicolas Dufresne
The vdec driver retrieves a canvas instance using meson_canvas_get() and
stores the raw pointer in core->canvas. However, the helper drops the
provider device reference via put_device() right before returning. Because
no formal device relationship is established, an unbind of the canvas
provider driver triggers a Use-After-Free (UAF) bug when the video decoder
subsequently attempts to access that memory block.
Fix this lifecycle hazard by enhancing meson_canvas_get() to establish a
formal managed device link between the consumer device and the underlying
canvas platform device. Using DL_FLAG_AUTOREMOVE_CONSUMER ensures the
dependency link is automatically torn down when the consumer driver
unbinds.
Additionally, handle missing link conditions using dev_err_probe() to
clean up upstream error diagnostics, and ensure the canvas platform device
reference count is dropped safely along the uninitialized driver data
path.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260530111022.9C6D71F00893@smtp.kernel.org
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/soc/amlogic/meson-canvas.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/soc/amlogic/meson-canvas.c b/drivers/soc/amlogic/meson-canvas.c
index 79681afea8c61..5102a491bf778 100644
--- a/drivers/soc/amlogic/meson-canvas.c
+++ b/drivers/soc/amlogic/meson-canvas.c
@@ -54,6 +54,7 @@ struct meson_canvas *meson_canvas_get(struct device *dev)
struct device_node *canvas_node;
struct platform_device *canvas_pdev;
struct meson_canvas *canvas;
+ struct device_link *link;
canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
if (!canvas_node)
@@ -70,9 +71,18 @@ struct meson_canvas *meson_canvas_get(struct device *dev)
* current state, this driver probe cannot return -EPROBE_DEFER
*/
canvas = dev_get_drvdata(&canvas_pdev->dev);
- put_device(&canvas_pdev->dev);
- if (!canvas)
+ if (!canvas) {
+ put_device(&canvas_pdev->dev);
return ERR_PTR(-EINVAL);
+ }
+
+ /* Establish device link to prevent Use-After-Free */
+ link = device_link_add(dev, &canvas_pdev->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+ put_device(&canvas_pdev->dev);
+ if (!link)
+ return ERR_PTR(dev_err_probe(dev, -EINVAL,
+ "Failed to create device link canvas\n"));
return canvas;
}
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 18/19] media: meson: vdec: Increase VIFIFO buffer size to 32 MiB
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne
During 4K video playback, the hardware Elementary Stream Parser (esparser)
can rapidly push massive burst data payloads into the Video Input FIFO
faster than the VPU decoding block can consume it. This causes a buffer
overflow when stream usage breaks past the allocated hardware boundary:
[ 1852.587956] meson-vdec ff620000.video-decoder: VIFIFO usage (16779003) > VIFIFO size (16777216)
When this overflow happens on Amlogic SoCs, it triggers an invalid memory
state, leading to stream corruption and fatal kernel panic lockups within
videobuf2 error rollback handling routines.
Double the parsed bitstream swap exchange memory pool (SIZE_VIFIFO) from
16 MiB (SZ_16M) to 32 MiB (SZ_32M). This provides a larger canvas window
to cushion high-frequency frame reordering spikes and prevents the VPU
from crashing on demanding modern streams.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index d33cbebc4453b..824e2f156adeb 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -29,8 +29,8 @@ struct dummy_buf {
struct list_head list;
};
-/* 16 MiB for parsed bitstream swap exchange */
-#define SIZE_VIFIFO SZ_16M
+/* 32 MiB for parsed bitstream swap exchange */
+#define SIZE_VIFIFO SZ_32M
static void vdec_free_canvas(struct amvdec_session *sess);
static void vdec_reset_timestamps(struct amvdec_session *sess);
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 18/19] media: meson: vdec: Increase VIFIFO buffer size to 32 MiB
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
During 4K video playback, the hardware Elementary Stream Parser (esparser)
can rapidly push massive burst data payloads into the Video Input FIFO
faster than the VPU decoding block can consume it. This causes a buffer
overflow when stream usage breaks past the allocated hardware boundary:
[ 1852.587956] meson-vdec ff620000.video-decoder: VIFIFO usage (16779003) > VIFIFO size (16777216)
When this overflow happens on Amlogic SoCs, it triggers an invalid memory
state, leading to stream corruption and fatal kernel panic lockups within
videobuf2 error rollback handling routines.
Double the parsed bitstream swap exchange memory pool (SIZE_VIFIFO) from
16 MiB (SZ_16M) to 32 MiB (SZ_32M). This provides a larger canvas window
to cushion high-frequency frame reordering spikes and prevents the VPU
from crashing on demanding modern streams.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index d33cbebc4453b..824e2f156adeb 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -29,8 +29,8 @@ struct dummy_buf {
struct list_head list;
};
-/* 16 MiB for parsed bitstream swap exchange */
-#define SIZE_VIFIFO SZ_16M
+/* 32 MiB for parsed bitstream swap exchange */
+#define SIZE_VIFIFO SZ_32M
static void vdec_free_canvas(struct amvdec_session *sess);
static void vdec_reset_timestamps(struct amvdec_session *sess);
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 18/19] media: meson: vdec: Increase VIFIFO buffer size to 32 MiB
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
During 4K video playback, the hardware Elementary Stream Parser (esparser)
can rapidly push massive burst data payloads into the Video Input FIFO
faster than the VPU decoding block can consume it. This causes a buffer
overflow when stream usage breaks past the allocated hardware boundary:
[ 1852.587956] meson-vdec ff620000.video-decoder: VIFIFO usage (16779003) > VIFIFO size (16777216)
When this overflow happens on Amlogic SoCs, it triggers an invalid memory
state, leading to stream corruption and fatal kernel panic lockups within
videobuf2 error rollback handling routines.
Double the parsed bitstream swap exchange memory pool (SIZE_VIFIFO) from
16 MiB (SZ_16M) to 32 MiB (SZ_32M). This provides a larger canvas window
to cushion high-frequency frame reordering spikes and prevents the VPU
from crashing on demanding modern streams.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index d33cbebc4453b..824e2f156adeb 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -29,8 +29,8 @@ struct dummy_buf {
struct list_head list;
};
-/* 16 MiB for parsed bitstream swap exchange */
-#define SIZE_VIFIFO SZ_16M
+/* 32 MiB for parsed bitstream swap exchange */
+#define SIZE_VIFIFO SZ_32M
static void vdec_free_canvas(struct amvdec_session *sess);
static void vdec_reset_timestamps(struct amvdec_session *sess);
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries
2026-07-13 12:06 ` Anand Moon
(?)
@ 2026-07-13 12:07 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Anand Moon, Doruk Tan Ozturk, Nicolas Dufresne
When importing DMABUFs exported by the Amlogic video decoder driver
(meson_vdec) for hardware-accelerated rendering paths, the DMA core
subsystem throws constraint validation warnings. This occurs because the
display controller master device lacks explicit DMA layout configuration,
causing it to fall back to a default 64KB maximum segment size limit.
Address these architectural constraints during the master bind sequence:
1. Initialize and validate a 32-bit coherent DMA allocation window by
invoking dma_set_mask_and_coherent() with a DMA_BIT_MASK(32) argument.
2. Maximize the contiguous scatter-gather allocation segment boundary
check constraint to UINT_MAX using the dma_set_max_seg_size() helper.
This guarantees that large video bitstream frame buffers can be imported
and scanned out across sub-driver domains without triggering allocation
warnings or page boundary splits.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/gpu/drm/meson/meson_drv.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
index 49ff9f1f16d32..899e70bca4ce2 100644
--- a/drivers/gpu/drm/meson/meson_drv.c
+++ b/drivers/gpu/drm/meson/meson_drv.c
@@ -202,6 +202,12 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
if (IS_ERR(drm))
return PTR_ERR(drm);
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ goto free_drm;
+
+ dma_set_max_seg_size(dev, UINT_MAX);
+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
When importing DMABUFs exported by the Amlogic video decoder driver
(meson_vdec) for hardware-accelerated rendering paths, the DMA core
subsystem throws constraint validation warnings. This occurs because the
display controller master device lacks explicit DMA layout configuration,
causing it to fall back to a default 64KB maximum segment size limit.
Address these architectural constraints during the master bind sequence:
1. Initialize and validate a 32-bit coherent DMA allocation window by
invoking dma_set_mask_and_coherent() with a DMA_BIT_MASK(32) argument.
2. Maximize the contiguous scatter-gather allocation segment boundary
check constraint to UINT_MAX using the dma_set_max_seg_size() helper.
This guarantees that large video bitstream frame buffers can be imported
and scanned out across sub-driver domains without triggering allocation
warnings or page boundary splits.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/gpu/drm/meson/meson_drv.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
index 49ff9f1f16d32..899e70bca4ce2 100644
--- a/drivers/gpu/drm/meson/meson_drv.c
+++ b/drivers/gpu/drm/meson/meson_drv.c
@@ -202,6 +202,12 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
if (IS_ERR(drm))
return PTR_ERR(drm);
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ goto free_drm;
+
+ dma_set_max_seg_size(dev, UINT_MAX);
+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
--
2.50.1
^ permalink raw reply related [flat|nested] 102+ messages in thread
* [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries
@ 2026-07-13 12:07 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-13 12:07 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk, Nicolas Dufresne
When importing DMABUFs exported by the Amlogic video decoder driver
(meson_vdec) for hardware-accelerated rendering paths, the DMA core
subsystem throws constraint validation warnings. This occurs because the
display controller master device lacks explicit DMA layout configuration,
causing it to fall back to a default 64KB maximum segment size limit.
Address these architectural constraints during the master bind sequence:
1. Initialize and validate a 32-bit coherent DMA allocation window by
invoking dma_set_mask_and_coherent() with a DMA_BIT_MASK(32) argument.
2. Maximize the contiguous scatter-gather allocation segment boundary
check constraint to UINT_MAX using the dma_set_max_seg_size() helper.
This guarantees that large video bitstream frame buffers can be imported
and scanned out across sub-driver domains without triggering allocation
warnings or page boundary splits.
Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/gpu/drm/meson/meson_drv.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
index 49ff9f1f16d32..899e70bca4ce2 100644
--- a/drivers/gpu/drm/meson/meson_drv.c
+++ b/drivers/gpu/drm/meson/meson_drv.c
@@ -202,6 +202,12 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
if (IS_ERR(drm))
return PTR_ERR(drm);
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ goto free_drm;
+
+ dma_set_max_seg_size(dev, UINT_MAX);
+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
--
2.50.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 102+ messages in thread
* Re: [PATCH v7 04/19] media: meson: vdec: Fix use-after-free race between teardown and ISR routines
2026-07-13 12:06 ` Anand Moon
@ 2026-07-13 12:23 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:23 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, linux-amlogic, dri-devel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Returning `IRQ_HANDLED` for a NULL session in the hardware interrupt handler causes an infinite interrupt storm.
Pre-existing issues:
- [High] Calling `synchronize_irq()` before stopping hardware interrupts fails to fix the use-after-free race condition.
- [High] Unbind operations destroy devm-managed structures and the m2m device while open file descriptors remain, causing UAF on closure.
--
--- Patch [4]: [PATCH v7 04/19] media: meson: vdec: Fix use-after-free race between teardown and ISR routines ---
commit 7f1533ed7c182234db28cc4195417115cb635bb9
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix use-after-free race between teardown and ISR routines
This patch attempts to prevent race conditions and null pointer dereferences
by synchronizing interrupt service routines during session teardown and file
descriptor release phases. It adds synchronize_irq() calls and uses
smp_load_acquire() to safely capture the current session in the ISR handlers.
Link: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index 7fc73d5cdebbf..7ae3d5a9dd6ab 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -484,6 +484,9 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> }
> }
>
> + /* Synchronize and flush pending hardware interrupt service routines */
> + synchronize_irq(core->vdec_irq);
> +
[Severity: High]
This isn't a bug introduced by this patch, but does calling synchronize_irq()
before vdec_poweroff() actually prevent the race condition?
Since synchronize_irq() only waits for currently executing handlers to finish
and does not disable the IRQ line or prevent new hardware interrupts, it seems
new interrupts can still fire immediately after it returns.
Because the hardware isn't stopped until vdec_poweroff() runs, could a newly
triggered ISR observe a valid session pointer and execute concurrently with the
rest of the teardown sequence?
Should the hardware be stopped via vdec_poweroff() before synchronizing IRQs?
> vdec_poweroff(sess);
> vdec_free_canvas(sess);
>
[ ... ]
> @@ -989,6 +992,9 @@ static int vdec_close(struct file *file)
> struct amvdec_session *sess = file_to_amvdec_session(file);
> struct amvdec_core *core = sess->core;
>
> + /* Synchronize and flush pending hardware interrupt service routines */
> + synchronize_irq(core->vdec_irq);
> +
[Severity: High]
This is a pre-existing issue, but does the driver handle unbind safely if
user-space keeps the video device file open?
If the driver is unbound, vdec_remove() executes:
vdec_remove()
video_unregister_device(core->vdev_dec);
v4l2_m2m_release(core->m2m_dev);
v4l2_device_unregister(&core->v4l2_dev);
Since devres automatically frees the amvdec_core structure upon unbind
completion, and video_unregister_device() does not forcefully close open file
descriptors, could a later call to vdec_close() dereference the freed core
structure?
For instance, this newly added synchronize_irq(core->vdec_irq) call would
use the freed core pointer. Similarly, v4l2_m2m_ctx_release(sess->m2m_ctx)
later in vdec_close() would use the freed core->m2m_dev.
> if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
> kthread_stop(sess->recycle_thread);
> sess->recycle_thread = NULL;
[ ... ]
> @@ -1038,7 +1044,12 @@ static const struct v4l2_file_operations vdec_fops = {
> static irqreturn_t vdec_isr(int irq, void *data)
> {
> struct amvdec_core *core = data;
> - struct amvdec_session *sess = core->cur_sess;
> + struct amvdec_session *sess;
> +
> + /* Secure an atomic acquire snapshot to protect against concurrent teardown */
> + sess = smp_load_acquire(&core->cur_sess);
> + if (!sess)
> + return IRQ_HANDLED;
[Severity: High]
Can returning IRQ_HANDLED here cause an infinite interrupt storm?
If the handler evaluates the session to NULL and returns IRQ_HANDLED without
acknowledging or clearing the interrupt at the hardware level, the generic IRQ
framework will unmask the interrupt line.
Since the hardware interrupt source hasn't been cleared, won't the interrupt
controller immediately re-trigger the interrupt, resulting in a soft lockup or
CPU stall?
Should this return IRQ_NONE so the IRQ subsystem can detect it as spurious and
eventually disable the IRQ line?
>
> sess->last_irq_jiffies = get_jiffies_64();
>
> @@ -1048,7 +1059,12 @@ static irqreturn_t vdec_isr(int irq, void *data)
> static irqreturn_t vdec_threaded_isr(int irq, void *data)
> {
> struct amvdec_core *core = data;
> - struct amvdec_session *sess = core->cur_sess;
> + struct amvdec_session *sess;
> +
> + /* Prevent late-stage threaded interrupts from dereferencing a NULL session */
> + sess = smp_load_acquire(&core->cur_sess);
> + if (!sess)
> + return IRQ_HANDLED;
[Severity: High]
Does returning IRQ_HANDLED here also incorrectly propagate the same interrupt
storm behavior?
>
> return sess->fmt_out->codec_ops->threaded_isr(sess);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=4
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 04/19] media: meson: vdec: Fix use-after-free race between teardown and ISR routines
@ 2026-07-13 12:23 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:23 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, linux-amlogic, dri-devel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Returning `IRQ_HANDLED` for a NULL session in the hardware interrupt handler causes an infinite interrupt storm.
Pre-existing issues:
- [High] Calling `synchronize_irq()` before stopping hardware interrupts fails to fix the use-after-free race condition.
- [High] Unbind operations destroy devm-managed structures and the m2m device while open file descriptors remain, causing UAF on closure.
--
--- Patch [4]: [PATCH v7 04/19] media: meson: vdec: Fix use-after-free race between teardown and ISR routines ---
commit 7f1533ed7c182234db28cc4195417115cb635bb9
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix use-after-free race between teardown and ISR routines
This patch attempts to prevent race conditions and null pointer dereferences
by synchronizing interrupt service routines during session teardown and file
descriptor release phases. It adds synchronize_irq() calls and uses
smp_load_acquire() to safely capture the current session in the ISR handlers.
Link: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index 7fc73d5cdebbf..7ae3d5a9dd6ab 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -484,6 +484,9 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> }
> }
>
> + /* Synchronize and flush pending hardware interrupt service routines */
> + synchronize_irq(core->vdec_irq);
> +
[Severity: High]
This isn't a bug introduced by this patch, but does calling synchronize_irq()
before vdec_poweroff() actually prevent the race condition?
Since synchronize_irq() only waits for currently executing handlers to finish
and does not disable the IRQ line or prevent new hardware interrupts, it seems
new interrupts can still fire immediately after it returns.
Because the hardware isn't stopped until vdec_poweroff() runs, could a newly
triggered ISR observe a valid session pointer and execute concurrently with the
rest of the teardown sequence?
Should the hardware be stopped via vdec_poweroff() before synchronizing IRQs?
> vdec_poweroff(sess);
> vdec_free_canvas(sess);
>
[ ... ]
> @@ -989,6 +992,9 @@ static int vdec_close(struct file *file)
> struct amvdec_session *sess = file_to_amvdec_session(file);
> struct amvdec_core *core = sess->core;
>
> + /* Synchronize and flush pending hardware interrupt service routines */
> + synchronize_irq(core->vdec_irq);
> +
[Severity: High]
This is a pre-existing issue, but does the driver handle unbind safely if
user-space keeps the video device file open?
If the driver is unbound, vdec_remove() executes:
vdec_remove()
video_unregister_device(core->vdev_dec);
v4l2_m2m_release(core->m2m_dev);
v4l2_device_unregister(&core->v4l2_dev);
Since devres automatically frees the amvdec_core structure upon unbind
completion, and video_unregister_device() does not forcefully close open file
descriptors, could a later call to vdec_close() dereference the freed core
structure?
For instance, this newly added synchronize_irq(core->vdec_irq) call would
use the freed core pointer. Similarly, v4l2_m2m_ctx_release(sess->m2m_ctx)
later in vdec_close() would use the freed core->m2m_dev.
> if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
> kthread_stop(sess->recycle_thread);
> sess->recycle_thread = NULL;
[ ... ]
> @@ -1038,7 +1044,12 @@ static const struct v4l2_file_operations vdec_fops = {
> static irqreturn_t vdec_isr(int irq, void *data)
> {
> struct amvdec_core *core = data;
> - struct amvdec_session *sess = core->cur_sess;
> + struct amvdec_session *sess;
> +
> + /* Secure an atomic acquire snapshot to protect against concurrent teardown */
> + sess = smp_load_acquire(&core->cur_sess);
> + if (!sess)
> + return IRQ_HANDLED;
[Severity: High]
Can returning IRQ_HANDLED here cause an infinite interrupt storm?
If the handler evaluates the session to NULL and returns IRQ_HANDLED without
acknowledging or clearing the interrupt at the hardware level, the generic IRQ
framework will unmask the interrupt line.
Since the hardware interrupt source hasn't been cleared, won't the interrupt
controller immediately re-trigger the interrupt, resulting in a soft lockup or
CPU stall?
Should this return IRQ_NONE so the IRQ subsystem can detect it as spurious and
eventually disable the IRQ line?
>
> sess->last_irq_jiffies = get_jiffies_64();
>
> @@ -1048,7 +1059,12 @@ static irqreturn_t vdec_isr(int irq, void *data)
> static irqreturn_t vdec_threaded_isr(int irq, void *data)
> {
> struct amvdec_core *core = data;
> - struct amvdec_session *sess = core->cur_sess;
> + struct amvdec_session *sess;
> +
> + /* Prevent late-stage threaded interrupts from dereferencing a NULL session */
> + sess = smp_load_acquire(&core->cur_sess);
> + if (!sess)
> + return IRQ_HANDLED;
[Severity: High]
Does returning IRQ_HANDLED here also incorrectly propagate the same interrupt
storm behavior?
>
> return sess->fmt_out->codec_ops->threaded_isr(sess);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=4
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 07/19] media: meson: vdec: Refactor esparser work queue and fix teardown race
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:27 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:27 UTC (permalink / raw)
To: Anand Moon; +Cc: dri-devel, linux-amlogic, neil.armstrong
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] The worker thread enters a 100% CPU infinite loop when the queue is empty, full, or hits an EOS buffer, and prematurely exits after a single frame on success due to an inverted loop condition and misuse of `scoped_guard`.
Pre-existing issues:
- [Critical] Maliciously crafted VP9 superframe headers can cause an integer overflow in `total_datasize`, bypassing bounds checks and triggering a massive out-of-bounds `memmove()`.
--
--- Patch [7]: [PATCH v7 07/19] media: meson: vdec: Refactor esparser work queue and fix teardown race ---
commit f4cf4afadbba1cd9ed8438a311d676dabd768d33
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Refactor esparser work queue and fix teardown race
Rework the esparser work queue logic to prevent concurrent queue mutations
and fix a race condition during streaming teardown.
> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> index 37749ede308c6..e5d3d817b9b2b 100644
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
[ ... ]
> @@ -348,12 +344,10 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
> if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
> payload_size = vp9_update_header(core, vb);
>
> - /* If unable to alter buffer to add headers */
> if (payload_size == 0) {
> + dev_err(core->dev, "esparser: VP9 header update failed\n");
> amvdec_remove_ts(sess, vb->timestamp);
> - v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> -
> - return 0;
> + return -EBADMSG;
> }
> }
[Severity: Critical]
This is a pre-existing issue, but can maliciously crafted VP9 superframe
headers cause an integer overflow in vp9_update_header(), leading to a
massive out-of-bounds memory copy?
Looking at vp9_update_header() in the same file:
for (cur_mag = 0; cur_mag < mag; cur_mag++) {
...
frame_size[cur_frame] |=
(dp[mag_ptr] << (cur_mag * 8));
mag_ptr++;
}
...
total_datasize += frame_size[cur_frame];
If untrusted user data shifted into the signed 32-bit frame_size becomes
negative, multiple negative values can sum to a small positive
total_datasize, which bypasses the size checks:
new_frame_size = total_datasize + num_frames * VP9_HEADER_SIZE;
if (new_frame_size >= vb2_plane_size(buf, 0)) {
return -ENOMEM;
}
Then, later in the same function:
int framesize = frame_size[cur_frame];
...
memmove(fdata + VP9_HEADER_SIZE, old_framedata, framesize);
The negative framesize is implicitly converted to a size_t, which causes
an out-of-bounds memmove(). Could this lead to memory corruption when
handling untrusted V4L2 video stream data?
[ ... ]
> void esparser_queue_all_src(struct work_struct *work)
> {
> - struct v4l2_m2m_buffer *buf, *n;
> struct amvdec_session *sess =
> container_of(work, struct amvdec_session, esparser_queue_work);
> + struct device *dev = sess->core->dev_dec;
> + int ret;
>
> - mutex_lock(&sess->lock);
> - v4l2_m2m_for_each_src_buf_safe(sess->m2m_ctx, buf, n) {
> - if (sess->should_stop)
> - break;
> + while (1) {
> + struct vb2_v4l2_buffer *vbuf = NULL;
> + bool processed_frame = false;
> +
> + scoped_guard(mutex, &sess->lock) {
> + /* Safe atomic tracking check: exit loop if session is shutting down */
> + if (sess->should_stop)
> + return;
> +
> + /* Queue completely empty: exit work loop cleanly */
> + vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
> + if (!vbuf)
> + break;
> +
> + /* Stop processing if we hit the end-of-stream drain buffer */
> + if (vbuf->flags & V4L2_BUF_FLAG_LAST)
> + break;
> +
> + /* Check hardware FIFO limits safely inside the locks */
> + ret = esparser_queue(sess, vbuf);
> + if (ret == -EAGAIN)
> + break;
> +
> + /* Pop the buffer from the source queue since it is now processed */
> + vbuf = v4l2_m2m_src_buf_remove(sess->m2m_ctx);
> + if (!vbuf) {
> + dev_dbg(dev, "Buffer missing during queue removal\n");
> + break;
> + }
> +
> + /* Complete the buffer transaction based on parser results */
> + if (ret < 0)
> + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> + else
> + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
>
> - if (esparser_queue(sess, &buf->vb) < 0)
> + /* Set tracking flag indicating transaction completion */
> + processed_frame = true;
> + }
> +
> + if (processed_frame)
> break;
[Severity: High]
Does this code inadvertently create an infinite loop?
Because scoped_guard() is implemented internally as a for-loop, the
break statements inside the scoped_guard() block will only exit the lock
scope, not the outer while (1) loop.
If the queue is empty (!vbuf), we break out of the scoped_guard(),
processed_frame remains false, and the while (1) loop continues infinitely,
consuming 100% CPU.
Conversely, if a frame is successfully processed, processed_frame becomes
true, which causes the outer while (1) loop to exit after just one frame
instead of continuing to drain the queue.
Could the logic here be inverted, or should the inner loop use goto
instead of break to exit the outer loop?
> +
> + /* Give other threads and IRQ routines a window to execute while unlocked */
> + cond_resched();
> }
> - mutex_unlock(&sess->lock);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=7
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 07/19] media: meson: vdec: Refactor esparser work queue and fix teardown race
@ 2026-07-13 12:27 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:27 UTC (permalink / raw)
To: Anand Moon; +Cc: dri-devel, linux-amlogic, neil.armstrong
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] The worker thread enters a 100% CPU infinite loop when the queue is empty, full, or hits an EOS buffer, and prematurely exits after a single frame on success due to an inverted loop condition and misuse of `scoped_guard`.
Pre-existing issues:
- [Critical] Maliciously crafted VP9 superframe headers can cause an integer overflow in `total_datasize`, bypassing bounds checks and triggering a massive out-of-bounds `memmove()`.
--
--- Patch [7]: [PATCH v7 07/19] media: meson: vdec: Refactor esparser work queue and fix teardown race ---
commit f4cf4afadbba1cd9ed8438a311d676dabd768d33
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Refactor esparser work queue and fix teardown race
Rework the esparser work queue logic to prevent concurrent queue mutations
and fix a race condition during streaming teardown.
> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> index 37749ede308c6..e5d3d817b9b2b 100644
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
[ ... ]
> @@ -348,12 +344,10 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
> if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
> payload_size = vp9_update_header(core, vb);
>
> - /* If unable to alter buffer to add headers */
> if (payload_size == 0) {
> + dev_err(core->dev, "esparser: VP9 header update failed\n");
> amvdec_remove_ts(sess, vb->timestamp);
> - v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> -
> - return 0;
> + return -EBADMSG;
> }
> }
[Severity: Critical]
This is a pre-existing issue, but can maliciously crafted VP9 superframe
headers cause an integer overflow in vp9_update_header(), leading to a
massive out-of-bounds memory copy?
Looking at vp9_update_header() in the same file:
for (cur_mag = 0; cur_mag < mag; cur_mag++) {
...
frame_size[cur_frame] |=
(dp[mag_ptr] << (cur_mag * 8));
mag_ptr++;
}
...
total_datasize += frame_size[cur_frame];
If untrusted user data shifted into the signed 32-bit frame_size becomes
negative, multiple negative values can sum to a small positive
total_datasize, which bypasses the size checks:
new_frame_size = total_datasize + num_frames * VP9_HEADER_SIZE;
if (new_frame_size >= vb2_plane_size(buf, 0)) {
return -ENOMEM;
}
Then, later in the same function:
int framesize = frame_size[cur_frame];
...
memmove(fdata + VP9_HEADER_SIZE, old_framedata, framesize);
The negative framesize is implicitly converted to a size_t, which causes
an out-of-bounds memmove(). Could this lead to memory corruption when
handling untrusted V4L2 video stream data?
[ ... ]
> void esparser_queue_all_src(struct work_struct *work)
> {
> - struct v4l2_m2m_buffer *buf, *n;
> struct amvdec_session *sess =
> container_of(work, struct amvdec_session, esparser_queue_work);
> + struct device *dev = sess->core->dev_dec;
> + int ret;
>
> - mutex_lock(&sess->lock);
> - v4l2_m2m_for_each_src_buf_safe(sess->m2m_ctx, buf, n) {
> - if (sess->should_stop)
> - break;
> + while (1) {
> + struct vb2_v4l2_buffer *vbuf = NULL;
> + bool processed_frame = false;
> +
> + scoped_guard(mutex, &sess->lock) {
> + /* Safe atomic tracking check: exit loop if session is shutting down */
> + if (sess->should_stop)
> + return;
> +
> + /* Queue completely empty: exit work loop cleanly */
> + vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
> + if (!vbuf)
> + break;
> +
> + /* Stop processing if we hit the end-of-stream drain buffer */
> + if (vbuf->flags & V4L2_BUF_FLAG_LAST)
> + break;
> +
> + /* Check hardware FIFO limits safely inside the locks */
> + ret = esparser_queue(sess, vbuf);
> + if (ret == -EAGAIN)
> + break;
> +
> + /* Pop the buffer from the source queue since it is now processed */
> + vbuf = v4l2_m2m_src_buf_remove(sess->m2m_ctx);
> + if (!vbuf) {
> + dev_dbg(dev, "Buffer missing during queue removal\n");
> + break;
> + }
> +
> + /* Complete the buffer transaction based on parser results */
> + if (ret < 0)
> + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> + else
> + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
>
> - if (esparser_queue(sess, &buf->vb) < 0)
> + /* Set tracking flag indicating transaction completion */
> + processed_frame = true;
> + }
> +
> + if (processed_frame)
> break;
[Severity: High]
Does this code inadvertently create an infinite loop?
Because scoped_guard() is implemented internally as a for-loop, the
break statements inside the scoped_guard() block will only exit the lock
scope, not the outer while (1) loop.
If the queue is empty (!vbuf), we break out of the scoped_guard(),
processed_frame remains false, and the while (1) loop continues infinitely,
consuming 100% CPU.
Conversely, if a frame is successfully processed, processed_frame becomes
true, which causes the outer while (1) loop to exit after just one frame
instead of continuing to drain the queue.
Could the logic here be inverted, or should the inner loop use goto
instead of break to exit the outer loop?
> +
> + /* Give other threads and IRQ routines a window to execute while unlocked */
> + cond_resched();
> }
> - mutex_unlock(&sess->lock);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=7
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 05/19] media: meson: vdec: Fix race condition and synchronize esparser IRQ
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:28 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:28 UTC (permalink / raw)
To: Anand Moon; +Cc: linux-amlogic, dri-devel, neil.armstrong
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] The newly added early return path in `esparser_isr()` incorrectly returns `IRQ_HANDLED` without clearing the hardware interrupt status, leading to an interrupt storm and violating shared IRQ semantics.
- [Medium] The commit message falsely claims to fix a race condition where the ISR 'attempts to handle stream data and allocate internal tracking state structures', which does not exist in the code.
Pre-existing issues:
- [High] The synchronization and early-exit logic introduced by the patch is ineffective and leaves the system vulnerable to synchronous bus faults during teardown.
- [High] Unconditional execution of `vdec_poweroff()` in `vdec_close()` causes clock underflows.
--
--- Patch [5]: [PATCH v7 05/19] media: meson: vdec: Fix race condition and synchronize esparser IRQ ---
commit 2f7e559e32b43d068f1e5390f023fbf7f75f1262
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix race condition and synchronize esparser IRQ
This commit attempts to fix a race condition during session teardown sequences
by synchronizing the esparser IRQ and adding a NULL check for the session
pointer in the ISR. It aims to prevent the interrupt handler from running
while context structures are dismantled.
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
> During session teardown sequences in vdec_stop_streaming() and
> vdec_close(), the 'esparser' hardware interrupt handler can still be
> actively triggered or executing on another CPU core. This creates a
> transient race condition where the ISR attempts to handle stream data and
> allocate internal tracking state structures after session contexts have
> been modified or freed.
[Severity: Medium]
Does the ISR actually allocate tracking state or handle stream data?
Looking at esparser_isr(), the sess pointer is retrieved and checked for NULL,
but the rest of the ISR does not appear to dereference this pointer or
allocate any internal tracking structures.
> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> index 4632346f04a9e..37749ede308c6 100644
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
> @@ -60,6 +60,12 @@ static irqreturn_t esparser_isr(int irq, void *dev)
> {
> int int_status;
> struct amvdec_core *core = dev;
> + struct amvdec_session *sess;
> +
> + /* Secure an atomic snapshot to protect against concurrent teardown */
> + sess = smp_load_acquire(&core->cur_sess);
> + if (!sess)
> + return IRQ_HANDLED;
[Severity: High]
Is it safe to return IRQ_HANDLED here without interacting with the hardware
to clear the interrupt?
Because esparser_irq is registered with IRQF_SHARED, returning IRQ_HANDLED
when cur_sess is NULL falsely tells the IRQ subsystem that this driver
handled the interrupt.
Will this cause an interrupt storm for level-triggered interrupts, or break
spurious interrupt detection for other devices sharing this IRQ line?
>
> int_status = amvdec_read_parser(core, PARSER_INT_STATUS);
> amvdec_write_parser(core, PARSER_INT_STATUS, int_status);
[ ... ]
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index 7ae3d5a9dd6ab..7689ffdb2e500 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -486,6 +486,8 @@ static void vdec_stop_streaming(struct vb2_queue *q)
>
> /* Synchronize and flush pending hardware interrupt service routines */
> synchronize_irq(core->vdec_irq);
> + /* Ensure esparser ISR finishes executing */
> + synchronize_irq(core->esparser_irq);
>
> vdec_poweroff(sess);
> vdec_free_canvas(sess);
[Severity: High]
This is a pre-existing issue, but does this synchronization logic fully
close the race condition?
The synchronize_irq() call waits for in-flight ISRs, but the interrupt line
remains enabled. Later in this teardown path:
vdec_stop_streaming()
vdec_poweroff(sess);
...
smp_store_release(&core->cur_sess, NULL);
If the interrupt fires after vdec_poweroff() completes, but before cur_sess
is set to NULL, the ISR will pass the !sess check and attempt to read
PARSER_INT_STATUS from unclocked hardware.
Could this lead to a synchronous bus fault and kernel panic?
[ ... ]
> @@ -994,6 +996,8 @@ static int vdec_close(struct file *file)
>
> /* Synchronize and flush pending hardware interrupt service routines */
> synchronize_irq(core->vdec_irq);
> + /* Ensure esparser ISR finishes executing */
> + synchronize_irq(core->esparser_irq);
>
> if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
> kthread_stop(sess->recycle_thread);
[Severity: High]
This is a pre-existing issue, but does vdec_close() unconditionally call
vdec_poweroff() even if the device was just opened without starting streaming?
If vdec_open() is called and immediately followed by vdec_close(), calling
vdec_poweroff() unconditionally would execute clk_disable_unprepare() on
dos_clk and dos_parser_clk which were never enabled.
Can this cause clock framework underflow warnings and potentially disable
clocks for other hardware sharing the same clock tree?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=5
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 05/19] media: meson: vdec: Fix race condition and synchronize esparser IRQ
@ 2026-07-13 12:28 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:28 UTC (permalink / raw)
To: Anand Moon; +Cc: linux-amlogic, dri-devel, neil.armstrong
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] The newly added early return path in `esparser_isr()` incorrectly returns `IRQ_HANDLED` without clearing the hardware interrupt status, leading to an interrupt storm and violating shared IRQ semantics.
- [Medium] The commit message falsely claims to fix a race condition where the ISR 'attempts to handle stream data and allocate internal tracking state structures', which does not exist in the code.
Pre-existing issues:
- [High] The synchronization and early-exit logic introduced by the patch is ineffective and leaves the system vulnerable to synchronous bus faults during teardown.
- [High] Unconditional execution of `vdec_poweroff()` in `vdec_close()` causes clock underflows.
--
--- Patch [5]: [PATCH v7 05/19] media: meson: vdec: Fix race condition and synchronize esparser IRQ ---
commit 2f7e559e32b43d068f1e5390f023fbf7f75f1262
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix race condition and synchronize esparser IRQ
This commit attempts to fix a race condition during session teardown sequences
by synchronizing the esparser IRQ and adding a NULL check for the session
pointer in the ISR. It aims to prevent the interrupt handler from running
while context structures are dismantled.
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
> During session teardown sequences in vdec_stop_streaming() and
> vdec_close(), the 'esparser' hardware interrupt handler can still be
> actively triggered or executing on another CPU core. This creates a
> transient race condition where the ISR attempts to handle stream data and
> allocate internal tracking state structures after session contexts have
> been modified or freed.
[Severity: Medium]
Does the ISR actually allocate tracking state or handle stream data?
Looking at esparser_isr(), the sess pointer is retrieved and checked for NULL,
but the rest of the ISR does not appear to dereference this pointer or
allocate any internal tracking structures.
> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> index 4632346f04a9e..37749ede308c6 100644
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
> @@ -60,6 +60,12 @@ static irqreturn_t esparser_isr(int irq, void *dev)
> {
> int int_status;
> struct amvdec_core *core = dev;
> + struct amvdec_session *sess;
> +
> + /* Secure an atomic snapshot to protect against concurrent teardown */
> + sess = smp_load_acquire(&core->cur_sess);
> + if (!sess)
> + return IRQ_HANDLED;
[Severity: High]
Is it safe to return IRQ_HANDLED here without interacting with the hardware
to clear the interrupt?
Because esparser_irq is registered with IRQF_SHARED, returning IRQ_HANDLED
when cur_sess is NULL falsely tells the IRQ subsystem that this driver
handled the interrupt.
Will this cause an interrupt storm for level-triggered interrupts, or break
spurious interrupt detection for other devices sharing this IRQ line?
>
> int_status = amvdec_read_parser(core, PARSER_INT_STATUS);
> amvdec_write_parser(core, PARSER_INT_STATUS, int_status);
[ ... ]
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index 7ae3d5a9dd6ab..7689ffdb2e500 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -486,6 +486,8 @@ static void vdec_stop_streaming(struct vb2_queue *q)
>
> /* Synchronize and flush pending hardware interrupt service routines */
> synchronize_irq(core->vdec_irq);
> + /* Ensure esparser ISR finishes executing */
> + synchronize_irq(core->esparser_irq);
>
> vdec_poweroff(sess);
> vdec_free_canvas(sess);
[Severity: High]
This is a pre-existing issue, but does this synchronization logic fully
close the race condition?
The synchronize_irq() call waits for in-flight ISRs, but the interrupt line
remains enabled. Later in this teardown path:
vdec_stop_streaming()
vdec_poweroff(sess);
...
smp_store_release(&core->cur_sess, NULL);
If the interrupt fires after vdec_poweroff() completes, but before cur_sess
is set to NULL, the ISR will pass the !sess check and attempt to read
PARSER_INT_STATUS from unclocked hardware.
Could this lead to a synchronous bus fault and kernel panic?
[ ... ]
> @@ -994,6 +996,8 @@ static int vdec_close(struct file *file)
>
> /* Synchronize and flush pending hardware interrupt service routines */
> synchronize_irq(core->vdec_irq);
> + /* Ensure esparser ISR finishes executing */
> + synchronize_irq(core->esparser_irq);
>
> if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
> kthread_stop(sess->recycle_thread);
[Severity: High]
This is a pre-existing issue, but does vdec_close() unconditionally call
vdec_poweroff() even if the device was just opened without starting streaming?
If vdec_open() is called and immediately followed by vdec_close(), calling
vdec_poweroff() unconditionally would execute clk_disable_unprepare() on
dos_clk and dos_parser_clk which were never enabled.
Can this cause clock framework underflow warnings and potentially disable
clocks for other hardware sharing the same clock tree?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=5
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 03/19] media: meson: vdec: Fix lifecycle leaks and race conditions in recycle_thread
2026-07-13 12:06 ` Anand Moon
@ 2026-07-13 12:32 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:32 UTC (permalink / raw)
To: Anand Moon; +Cc: linux-amlogic, dri-devel, neil.armstrong
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [Critical] vdec_close() frees session memory before releasing the V4L2 M2M context, triggering a NULL pointer dereference during queue teardown.
- [Critical] Unconditional execution of vdec_poweroff() and clearing of core->cur_sess in vdec_close() causes Data Abort panics and cross-session corruption.
- [Critical] Unconditional execution of vdec_poweroff() in vdec_close() leads to a double free of codec DMA allocations.
- [High] vdec_close() prematurely frees session context and DMA buffers without canceling background workqueues, causing Use-After-Free and DMA corruption.
Pre-existing issues:
- [High] Flawed logic in vdec_stop_streaming() orphans the recycle thread, leading to Use-After-Free during partial STREAMOFF.
- [Medium] The err_poweroff unwinding path in vdec_start_streaming() leaks sess->priv and associated codec assets.
--
--- Patch [3]: [PATCH v7 03/19] media: meson: vdec: Fix lifecycle leaks and race conditions in recycle_thread ---
commit 41dac138c8515aa623b248b4a110d8a7802b604b
Author: Anand Moon <linux.amoon@gmail.com>
Subject: media: meson: vdec: Fix lifecycle leaks and race conditions in recycle_thread
This patch validates the kthread_run return status during recycling thread
initialization to prevent memory leaks. It also ensures the thread is safely
stopped in both vdec_stop_streaming and vdec_close to prevent kernel panics
during teardown.
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
[ ... ]
@@ -353,13 +357,23 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
> schedule_work(&sess->esparser_queue_work);
> return 0;
>
> err_poweroff:
> vdec_poweroff(sess);
> vdec_free_canvas(sess);
> vdec_reset_timestamps(sess);
> vdec_reset_bufs_recycle(sess);
[Severity: Medium]
This is a pre-existing issue, but does the err_poweroff unwinding path leak
sess->priv?
If kthread_run() fails, execution jumps to the err_poweroff label which
unwinds various hardware states but misses calling kfree(sess->priv).
When the application retries VIDIOC_STREAMON, the codec's start routine
unconditionally allocates a new structure over sess->priv, permanently
stranding the previous allocation.
> err_free_vififo:
> if (sess->vififo_vaddr) {
> dma_free_coherent(sess->core->dev, sess->vififo_size,
[ ... ]
@@ -464,7 +478,10 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> if (full_cleanup) {
> if ((q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
> !sess->streamon_out) && vdec_codec_needs_recycle(sess)) {
[Severity: High]
This is a pre-existing issue, but does this logic orphan the recycle thread
leading to a use-after-free during partial STREAMOFF?
If userspace invokes VIDIOC_STREAMOFF exclusively on the capture queue
while the output queue remains active, this condition evaluates to false,
bypassing the kthread_stop() call.
Because old_status == STATUS_RUNNING, full_cleanup evaluates to true,
proceeding to dismantle the session context and free sess->priv.
The orphaned recycle thread may concurrently awaken and attempt to access
this freed memory.
> - kthread_stop(sess->recycle_thread);
> + if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
> + kthread_stop(sess->recycle_thread);
> + sess->recycle_thread = NULL;
> + }
> }
[ ... ]
@@ -970,6 +987,31 @@ static int vdec_open(struct file *file)
> static int vdec_close(struct file *file)
> {
> struct amvdec_session *sess = file_to_amvdec_session(file);
> + struct amvdec_core *core = sess->core;
> +
> + if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
> + kthread_stop(sess->recycle_thread);
> + sess->recycle_thread = NULL;
> + }
> +
> + mutex_lock(&core->lock);
> +
> + vdec_poweroff(sess);
> + vdec_free_canvas(sess);
> + core->cur_sess = NULL;
[Severity: Critical]
Can this unconditional invocation of vdec_poweroff() and clearing of
core->cur_sess cause Data Abort panics and cross-session corruption?
If userspace opens two instances (FDs), starts streaming on instance A
(acquiring hardware ownership), and then closes instance B:
vdec_close() for instance B will unconditionally call vdec_poweroff(sess)
and clear core->cur_sess, without verifying if instance B actually owns the
hardware.
This forces clock disablements while instance A is actively decoding. When
the hardware for instance A fires an interrupt, vdec_isr() will dereference
core->cur_sess, which was just wrongfully set to NULL, causing a panic.
[Severity: Critical]
Will this unconditional vdec_poweroff() call also lead to a double free of
codec DMA allocations?
If userspace cleanly shuts down streaming via VIDIOC_STREAMOFF,
vdec_stop_streaming() executes vdec_poweroff(). This invokes the codec
stop callback (like codec_h264_stop) which frees memory buffers but does
not set their pointers to NULL.
When userspace subsequently calls close(), vdec_close() calls
vdec_poweroff() again. The codec stop routine will execute a second time,
passing the same non-NULL pointers to dma_free_coherent() resulting in a
catastrophic double-free.
> +
> + if (sess->vififo_vaddr) {
> + dma_free_coherent(core->dev, sess->vififo_size,
> + sess->vififo_vaddr, sess->vififo_paddr);
> + sess->vififo_vaddr = NULL;
> + sess->vififo_paddr = 0;
> + }
> + vdec_reset_timestamps(sess);
> + vdec_reset_bufs_recycle(sess);
> + kfree(sess->priv);
[Severity: High]
Should we call cancel_work_sync(&sess->esparser_queue_work) before freeing
the session context and DMA buffers here?
If vdec_close() is called while the background workqueue is actively
processing frames, the DMA buffer (sess->vififo_vaddr) and session
structure (sess->priv) are immediately freed.
The concurrently executing workqueue continues its operation inside
esparser_queue(), dereferencing the freed sess->priv pointer and
instructing the hardware to perform DMA transfers into the freshly freed
physical address, potentially causing severe memory corruption.
> + sess->priv = NULL;
> +
> + mutex_unlock(&core->lock);
>
> v4l2_m2m_ctx_release(sess->m2m_ctx);
[Severity: Critical]
Does this sequence trigger a NULL pointer dereference during queue teardown?
If a user closes the file descriptor without calling VIDIOC_STREAMOFF,
vdec_close() will free sess->priv and set it to NULL.
Immediately after, it calls v4l2_m2m_ctx_release(), which triggers
vdec_stop_streaming(). Since sess->status is still STATUS_RUNNING, the
driver will execute codec_ops->drain(sess).
The codec's drain function unconditionally dereferences sess->priv, which
was just set to NULL, causing an immediate panic.
> v4l2_fh_del(&sess->fh, file);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=3
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 03/19] media: meson: vdec: Fix lifecycle leaks and race conditions in recycle_thread
@ 2026-07-13 12:32 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:32 UTC (permalink / raw)
To: Anand Moon; +Cc: linux-amlogic, dri-devel, neil.armstrong
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [Critical] vdec_close() frees session memory before releasing the V4L2 M2M context, triggering a NULL pointer dereference during queue teardown.
- [Critical] Unconditional execution of vdec_poweroff() and clearing of core->cur_sess in vdec_close() causes Data Abort panics and cross-session corruption.
- [Critical] Unconditional execution of vdec_poweroff() in vdec_close() leads to a double free of codec DMA allocations.
- [High] vdec_close() prematurely frees session context and DMA buffers without canceling background workqueues, causing Use-After-Free and DMA corruption.
Pre-existing issues:
- [High] Flawed logic in vdec_stop_streaming() orphans the recycle thread, leading to Use-After-Free during partial STREAMOFF.
- [Medium] The err_poweroff unwinding path in vdec_start_streaming() leaks sess->priv and associated codec assets.
--
--- Patch [3]: [PATCH v7 03/19] media: meson: vdec: Fix lifecycle leaks and race conditions in recycle_thread ---
commit 41dac138c8515aa623b248b4a110d8a7802b604b
Author: Anand Moon <linux.amoon@gmail.com>
Subject: media: meson: vdec: Fix lifecycle leaks and race conditions in recycle_thread
This patch validates the kthread_run return status during recycling thread
initialization to prevent memory leaks. It also ensures the thread is safely
stopped in both vdec_stop_streaming and vdec_close to prevent kernel panics
during teardown.
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
[ ... ]
@@ -353,13 +357,23 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
> schedule_work(&sess->esparser_queue_work);
> return 0;
>
> err_poweroff:
> vdec_poweroff(sess);
> vdec_free_canvas(sess);
> vdec_reset_timestamps(sess);
> vdec_reset_bufs_recycle(sess);
[Severity: Medium]
This is a pre-existing issue, but does the err_poweroff unwinding path leak
sess->priv?
If kthread_run() fails, execution jumps to the err_poweroff label which
unwinds various hardware states but misses calling kfree(sess->priv).
When the application retries VIDIOC_STREAMON, the codec's start routine
unconditionally allocates a new structure over sess->priv, permanently
stranding the previous allocation.
> err_free_vififo:
> if (sess->vififo_vaddr) {
> dma_free_coherent(sess->core->dev, sess->vififo_size,
[ ... ]
@@ -464,7 +478,10 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> if (full_cleanup) {
> if ((q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
> !sess->streamon_out) && vdec_codec_needs_recycle(sess)) {
[Severity: High]
This is a pre-existing issue, but does this logic orphan the recycle thread
leading to a use-after-free during partial STREAMOFF?
If userspace invokes VIDIOC_STREAMOFF exclusively on the capture queue
while the output queue remains active, this condition evaluates to false,
bypassing the kthread_stop() call.
Because old_status == STATUS_RUNNING, full_cleanup evaluates to true,
proceeding to dismantle the session context and free sess->priv.
The orphaned recycle thread may concurrently awaken and attempt to access
this freed memory.
> - kthread_stop(sess->recycle_thread);
> + if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
> + kthread_stop(sess->recycle_thread);
> + sess->recycle_thread = NULL;
> + }
> }
[ ... ]
@@ -970,6 +987,31 @@ static int vdec_open(struct file *file)
> static int vdec_close(struct file *file)
> {
> struct amvdec_session *sess = file_to_amvdec_session(file);
> + struct amvdec_core *core = sess->core;
> +
> + if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
> + kthread_stop(sess->recycle_thread);
> + sess->recycle_thread = NULL;
> + }
> +
> + mutex_lock(&core->lock);
> +
> + vdec_poweroff(sess);
> + vdec_free_canvas(sess);
> + core->cur_sess = NULL;
[Severity: Critical]
Can this unconditional invocation of vdec_poweroff() and clearing of
core->cur_sess cause Data Abort panics and cross-session corruption?
If userspace opens two instances (FDs), starts streaming on instance A
(acquiring hardware ownership), and then closes instance B:
vdec_close() for instance B will unconditionally call vdec_poweroff(sess)
and clear core->cur_sess, without verifying if instance B actually owns the
hardware.
This forces clock disablements while instance A is actively decoding. When
the hardware for instance A fires an interrupt, vdec_isr() will dereference
core->cur_sess, which was just wrongfully set to NULL, causing a panic.
[Severity: Critical]
Will this unconditional vdec_poweroff() call also lead to a double free of
codec DMA allocations?
If userspace cleanly shuts down streaming via VIDIOC_STREAMOFF,
vdec_stop_streaming() executes vdec_poweroff(). This invokes the codec
stop callback (like codec_h264_stop) which frees memory buffers but does
not set their pointers to NULL.
When userspace subsequently calls close(), vdec_close() calls
vdec_poweroff() again. The codec stop routine will execute a second time,
passing the same non-NULL pointers to dma_free_coherent() resulting in a
catastrophic double-free.
> +
> + if (sess->vififo_vaddr) {
> + dma_free_coherent(core->dev, sess->vififo_size,
> + sess->vififo_vaddr, sess->vififo_paddr);
> + sess->vififo_vaddr = NULL;
> + sess->vififo_paddr = 0;
> + }
> + vdec_reset_timestamps(sess);
> + vdec_reset_bufs_recycle(sess);
> + kfree(sess->priv);
[Severity: High]
Should we call cancel_work_sync(&sess->esparser_queue_work) before freeing
the session context and DMA buffers here?
If vdec_close() is called while the background workqueue is actively
processing frames, the DMA buffer (sess->vififo_vaddr) and session
structure (sess->priv) are immediately freed.
The concurrently executing workqueue continues its operation inside
esparser_queue(), dereferencing the freed sess->priv pointer and
instructing the hardware to perform DMA transfers into the freshly freed
physical address, potentially causing severe memory corruption.
> + sess->priv = NULL;
> +
> + mutex_unlock(&core->lock);
>
> v4l2_m2m_ctx_release(sess->m2m_ctx);
[Severity: Critical]
Does this sequence trigger a NULL pointer dereference during queue teardown?
If a user closes the file descriptor without calling VIDIOC_STREAMOFF,
vdec_close() will free sess->priv and set it to NULL.
Immediately after, it calls v4l2_m2m_ctx_release(), which triggers
vdec_stop_streaming(). Since sess->status is still STATUS_RUNNING, the
driver will execute codec_ops->drain(sess).
The codec's drain function unconditionally dereferences sess->priv, which
was just set to NULL, causing an immediate panic.
> v4l2_fh_del(&sess->fh, file);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=3
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 06/19] media: meson: vdec: Fix race condition by canceling work sync
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:33 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:33 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, dri-devel, linux-amlogic
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Calling `cancel_work_sync(&sess->esparser_queue_work)` inside `vdec_stop_streaming()` introduces a guaranteed AB-BA deadlock during `VIDIOC_STREAMOFF`.
- [High] `cancel_work_sync()` and `synchronize_irq()` are invoked before the hardware is powered off, leaving a race window where late interrupts can schedule work on a soon-to-be-freed session.
- [High] `vdec_close()` unconditionally powers off shared hardware state and clears the active session, allowing a rogue client to crash an ongoing decoding session.
- [Medium] The patch fails to remove the scheduling of `esparser_queue_work` during destination buffer completion, contradicting its own commit message.
--
--- Patch [6]: [PATCH v7 06/19] media: meson: vdec: Fix race condition by canceling work sync ---
commit 9631e0314174ce9dbe778bf9146832ff6e5a0364
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix race condition by canceling work sync
This commit attempts to synchronize workqueue states during teardown to
eliminate race conditions. It adds synchronous cancellation of
esparser_queue_work in vdec_stop_streaming() and vdec_close().
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
> Remove the scheduling of esparser queue work during destination buffer
> completion, as freeing the vififo is handled elsewhere.
[Severity: Medium]
Did this actually get removed? Looking at dst_buf_done() in vdec_helpers.c,
it still contains:
/* Buffer done probably means the vififo got freed */
schedule_work(&sess->esparser_queue_work);
This seems to contradict the commit message and means the workqueue is still
scheduled asynchronously during destination buffer completion.
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index 7689ffdb2e500..6fe9722577179 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -484,11 +484,15 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> }
> }
>
> + mutex_unlock(&core->lock);
> /* Synchronize and flush pending hardware interrupt service routines */
> synchronize_irq(core->vdec_irq);
> /* Ensure esparser ISR finishes executing */
> synchronize_irq(core->esparser_irq);
>
> + cancel_work_sync(&sess->esparser_queue_work);
[Severity: High]
Can this cause a deadlock?
When userspace calls the streamoff ioctl, the v4l2 core acquires the queue
lock (&sess->lock) before calling this vdec_stop_streaming() callback.
Since cancel_work_sync() waits for the worker thread to finish, and the
worker esparser_queue_all_src() attempts to acquire mutex_lock(&sess->lock),
won't these threads wait on each other indefinitely if the worker is
already running?
[Severity: High]
Could this teardown ordering lead to a use-after-free?
Because cancel_work_sync() and synchronize_irq() are called before
vdec_poweroff() disables the hardware, the hardware is still active
and can generate interrupts. An interrupt could fire immediately after
cancel_work_sync() returns, causing dst_buf_done() to schedule the work
again. If the sess structure is subsequently freed, won't the newly
scheduled work dereference a freed pointer?
> + mutex_lock(&core->lock);
> +
> vdec_poweroff(sess);
> vdec_free_canvas(sess);
[ ... ]
> @@ -999,6 +1003,8 @@ static int vdec_close(struct file *file)
> /* Ensure esparser ISR finishes executing */
> synchronize_irq(core->esparser_irq);
>
> + cancel_work_sync(&sess->esparser_queue_work);
[Severity: High]
Does this have the same teardown ordering race as vdec_stop_streaming()?
The cancel_work_sync() call happens before vdec_poweroff(sess). If
the hardware generates a late interrupt, could the work be scheduled
again right before the session is freed?
> +
> if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
> kthread_stop(sess->recycle_thread);
> sess->recycle_thread = NULL;
Later in vdec_close(), the hardware is powered down and the session is cleared:
mutex_lock(&core->lock);
vdec_poweroff(sess);
vdec_free_canvas(sess);
core->cur_sess = NULL;
[Severity: High]
Does this unconditionally terminate the hardware state of the active session?
Because this lacks an if (core->cur_sess == sess) check (which is safely used
in vdec_stop_streaming), if a secondary client opens and immediately closes
the device node, won't it forcefully power off the hardware and clear the
session while the primary client is actively streaming?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=6
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 06/19] media: meson: vdec: Fix race condition by canceling work sync
@ 2026-07-13 12:33 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:33 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, dri-devel, linux-amlogic
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Calling `cancel_work_sync(&sess->esparser_queue_work)` inside `vdec_stop_streaming()` introduces a guaranteed AB-BA deadlock during `VIDIOC_STREAMOFF`.
- [High] `cancel_work_sync()` and `synchronize_irq()` are invoked before the hardware is powered off, leaving a race window where late interrupts can schedule work on a soon-to-be-freed session.
- [High] `vdec_close()` unconditionally powers off shared hardware state and clears the active session, allowing a rogue client to crash an ongoing decoding session.
- [Medium] The patch fails to remove the scheduling of `esparser_queue_work` during destination buffer completion, contradicting its own commit message.
--
--- Patch [6]: [PATCH v7 06/19] media: meson: vdec: Fix race condition by canceling work sync ---
commit 9631e0314174ce9dbe778bf9146832ff6e5a0364
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix race condition by canceling work sync
This commit attempts to synchronize workqueue states during teardown to
eliminate race conditions. It adds synchronous cancellation of
esparser_queue_work in vdec_stop_streaming() and vdec_close().
Closes: https://lore.kernel.org/all/20260521090944.F35401F00A3D@smtp.kernel.org/
> Remove the scheduling of esparser queue work during destination buffer
> completion, as freeing the vififo is handled elsewhere.
[Severity: Medium]
Did this actually get removed? Looking at dst_buf_done() in vdec_helpers.c,
it still contains:
/* Buffer done probably means the vififo got freed */
schedule_work(&sess->esparser_queue_work);
This seems to contradict the commit message and means the workqueue is still
scheduled asynchronously during destination buffer completion.
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index 7689ffdb2e500..6fe9722577179 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -484,11 +484,15 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> }
> }
>
> + mutex_unlock(&core->lock);
> /* Synchronize and flush pending hardware interrupt service routines */
> synchronize_irq(core->vdec_irq);
> /* Ensure esparser ISR finishes executing */
> synchronize_irq(core->esparser_irq);
>
> + cancel_work_sync(&sess->esparser_queue_work);
[Severity: High]
Can this cause a deadlock?
When userspace calls the streamoff ioctl, the v4l2 core acquires the queue
lock (&sess->lock) before calling this vdec_stop_streaming() callback.
Since cancel_work_sync() waits for the worker thread to finish, and the
worker esparser_queue_all_src() attempts to acquire mutex_lock(&sess->lock),
won't these threads wait on each other indefinitely if the worker is
already running?
[Severity: High]
Could this teardown ordering lead to a use-after-free?
Because cancel_work_sync() and synchronize_irq() are called before
vdec_poweroff() disables the hardware, the hardware is still active
and can generate interrupts. An interrupt could fire immediately after
cancel_work_sync() returns, causing dst_buf_done() to schedule the work
again. If the sess structure is subsequently freed, won't the newly
scheduled work dereference a freed pointer?
> + mutex_lock(&core->lock);
> +
> vdec_poweroff(sess);
> vdec_free_canvas(sess);
[ ... ]
> @@ -999,6 +1003,8 @@ static int vdec_close(struct file *file)
> /* Ensure esparser ISR finishes executing */
> synchronize_irq(core->esparser_irq);
>
> + cancel_work_sync(&sess->esparser_queue_work);
[Severity: High]
Does this have the same teardown ordering race as vdec_stop_streaming()?
The cancel_work_sync() call happens before vdec_poweroff(sess). If
the hardware generates a late interrupt, could the work be scheduled
again right before the session is freed?
> +
> if (!IS_ERR_OR_NULL(sess->recycle_thread)) {
> kthread_stop(sess->recycle_thread);
> sess->recycle_thread = NULL;
Later in vdec_close(), the hardware is powered down and the session is cleared:
mutex_lock(&core->lock);
vdec_poweroff(sess);
vdec_free_canvas(sess);
core->cur_sess = NULL;
[Severity: High]
Does this unconditionally terminate the hardware state of the active session?
Because this lacks an if (core->cur_sess == sess) check (which is safely used
in vdec_stop_streaming), if a secondary client opens and immediately closes
the device node, won't it forcefully power off the hardware and clear the
session while the primary client is actively streaming?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=6
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 01/19] media: meson: vdec: Fix m2m device lifetime and cleanup path
2026-07-13 12:06 ` Anand Moon
@ 2026-07-13 12:35 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:35 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, dri-devel, linux-amlogic
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Moving `v4l2_m2m_dev` allocation to the driver core introduces an exploitable Use-After-Free (UAF) during device removal when file descriptors remain open.
Pre-existing issues:
- [High] The `amvdec_core` structure is improperly allocated using `devm_kzalloc`, leading to Use-After-Free bugs when the device is unbound.
--
--- Patch [1]: [PATCH v7 01/19] media: meson: vdec: Fix m2m device lifetime and cleanup path ---
commit f97dc3a43da5c78cbae7f710eb7770593a75ff3e
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix m2m device lifetime and cleanup path
The vdec driver was incorrectly initializing a new v4l2_m2m device
instance per session inside vdec_open() and releasing it in vdec_close().
This design is faulty because the m2m device models the core hardware
engine and must persist across multiple sessions.
Closes: https://lore.kernel.org/all/20260520045905.6ACBA1F000E9@smtp.kernel.org/#t
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index a039d925c0fe5..6ae3471155a87 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
[ ... ]
> @@ -1105,6 +1106,7 @@ static void vdec_remove(struct platform_device *pdev)
> struct amvdec_core *core = platform_get_drvdata(pdev);
>
> video_unregister_device(core->vdev_dec);
> + v4l2_m2m_release(core->m2m_dev);
[Severity: High]
Does releasing the m2m device here introduce a use-after-free if a file
descriptor remains open?
If a user unbinds the driver, vdec_remove() synchronously frees core->m2m_dev.
However, if an application still holds a file descriptor open, it will
eventually close it, triggering vdec_close():
vdec_close()
v4l2_m2m_ctx_release(sess->m2m_ctx)
v4l2_m2m_cancel_job()
spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
Since core->m2m_dev was already freed during unbind, could this dereference
cause memory corruption or a crash?
[Severity: High]
This is a pre-existing issue, but does returning from vdec_remove() without
waiting for open file descriptors to close lead to a use-after-free of the
core driver structure?
The core structure and its mutex are allocated via devres (devm_kzalloc)
during vdec_probe(). If the device is unbound, devres will automatically free
the core structure as soon as vdec_remove() returns.
If an ioctl is currently blocked waiting on vdev->lock (which points to
core->lock), it will attempt to unlock a freed mutex when it resumes.
Additionally, if the asynchronous esparser_queue_work runs after unbind, it
will dereference the freed sess->core.
Could the driver synchronize active sessions or use a different lifecycle for
the core structure to prevent this?
> v4l2_device_unregister(&core->v4l2_dev);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=1
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 01/19] media: meson: vdec: Fix m2m device lifetime and cleanup path
@ 2026-07-13 12:35 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:35 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, dri-devel, linux-amlogic
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Moving `v4l2_m2m_dev` allocation to the driver core introduces an exploitable Use-After-Free (UAF) during device removal when file descriptors remain open.
Pre-existing issues:
- [High] The `amvdec_core` structure is improperly allocated using `devm_kzalloc`, leading to Use-After-Free bugs when the device is unbound.
--
--- Patch [1]: [PATCH v7 01/19] media: meson: vdec: Fix m2m device lifetime and cleanup path ---
commit f97dc3a43da5c78cbae7f710eb7770593a75ff3e
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix m2m device lifetime and cleanup path
The vdec driver was incorrectly initializing a new v4l2_m2m device
instance per session inside vdec_open() and releasing it in vdec_close().
This design is faulty because the m2m device models the core hardware
engine and must persist across multiple sessions.
Closes: https://lore.kernel.org/all/20260520045905.6ACBA1F000E9@smtp.kernel.org/#t
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index a039d925c0fe5..6ae3471155a87 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
[ ... ]
> @@ -1105,6 +1106,7 @@ static void vdec_remove(struct platform_device *pdev)
> struct amvdec_core *core = platform_get_drvdata(pdev);
>
> video_unregister_device(core->vdev_dec);
> + v4l2_m2m_release(core->m2m_dev);
[Severity: High]
Does releasing the m2m device here introduce a use-after-free if a file
descriptor remains open?
If a user unbinds the driver, vdec_remove() synchronously frees core->m2m_dev.
However, if an application still holds a file descriptor open, it will
eventually close it, triggering vdec_close():
vdec_close()
v4l2_m2m_ctx_release(sess->m2m_ctx)
v4l2_m2m_cancel_job()
spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
Since core->m2m_dev was already freed during unbind, could this dereference
cause memory corruption or a crash?
[Severity: High]
This is a pre-existing issue, but does returning from vdec_remove() without
waiting for open file descriptors to close lead to a use-after-free of the
core driver structure?
The core structure and its mutex are allocated via devres (devm_kzalloc)
during vdec_probe(). If the device is unbound, devres will automatically free
the core structure as soon as vdec_remove() returns.
If an ioctl is currently blocked waiting on vdev->lock (which points to
core->lock), it will attempt to unlock a freed mutex when it resumes.
Additionally, if the asynchronous esparser_queue_work runs after unbind, it
will dereference the freed sess->core.
Could the driver synchronize active sessions or use a different lifecycle for
the core structure to prevent this?
> v4l2_device_unregister(&core->v4l2_dev);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 02/19] media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown
2026-07-13 12:06 ` Anand Moon
@ 2026-07-13 12:38 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:38 UTC (permalink / raw)
To: Anand Moon; +Cc: linux-amlogic, neil.armstrong, dri-devel
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [High] The vdec_recycle_thread is conditionally skipped from being stopped but its resources are unconditionally freed during a partial STREAMOFF, leading to Use-After-Free and list corruption.
- [High] Failing start_streaming with -EBUSY permanently leaks streamon flags, causing state inconsistency and cross-session hardware corruption.
Pre-existing issues:
- [High] The return value of `kthread_run()` is not checked, leading to a kernel crash when `kthread_stop()` is called on an `ERR_PTR`.
- [High] vdec_reset_timestamps accesses the timestamps list without holding ts_spinlock, leading to list corruption.
- [High] Missing cancel_work_sync for esparser_queue_work causes UAF and unclocked register access.
--
--- Patch [2]: [PATCH v7 02/19] media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown ---
commit c8f8b26e4a120933b5b1d329b3cc6e97ea21778d
Author: Anand Moon <linux.amoon@gmail.com>
Subject: media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown
This commit addresses race conditions during concurrent STREAMON / STREAMOFF
calls and dynamic resolution changes. It restructures initialization paths,
error handling, and state tracking to prevent hardware state corruption.
Closes: https://lore.kernel.org/all/20260530100841.9CEBA1F00893@smtp.kernel.org/
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
[ ... ]
> @@ -308,9 +303,29 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
> }
>
> if (sess->status == STATUS_RUNNING ||
> - sess->status == STATUS_NEEDS_RESUME ||
> - sess->status == STATUS_INIT)
> + sess->status == STATUS_NEEDS_RESUME)
> return 0;
>
> + /*
> + * Secure the core hardware lock before checking availability
> + * and updating session states to prevent STREAMON race conditions.
> + */
> + mutex_lock(&core->lock);
> + if (core->cur_sess && core->cur_sess != sess) {
> + ret = -EBUSY;
> + mutex_unlock(&core->lock);
> + goto err_unlock_no_hw;
> + }
[Severity: High]
If we jump to err_unlock_no_hw here, doesn't it bypass the
err_cleanup_session label that clears the streamon flags?
If session B calls VIDIOC_STREAMON on its OUTPUT queue and hits this path,
sess->streamon_out will remain 1.
Could a subsequent VIDIOC_QBUF from session B cause vdec_vb2_buf_queue to
schedule esparser_queue_work, potentially overwriting the active hardware
registers of the current session?
[ ... ]
> sess->sequence_cap = 0;
> sess->sequence_out = 0;
> +
> if (vdec_codec_needs_recycle(sess))
> sess->recycle_thread = kthread_run(vdec_recycle_thread, sess,
> "vdec_recycle");
[Severity: High]
This is a pre-existing issue, but does this code properly handle thread
creation failure?
If kthread_run fails due to memory constraints, it returns an ERR_PTR.
Later in vdec_stop_streaming, this pointer is passed unconditionally to
kthread_stop.
Could this cause a kernel panic when kthread_stop attempts to dereference
the ERR_PTR?
> - sess->status = STATUS_INIT;
> - core->cur_sess = sess;
> schedule_work(&sess->esparser_queue_work);
> return 0;
[Severity: High]
This is a pre-existing issue, but does closing the file descriptor safely
clean up the workqueue scheduled here?
When the client queues a buffer and then closes the file descriptor,
vdec_close destroys mutexes and frees the amvdec_session memory via kfree
without cancelling the pending esparser_queue_work.
When the workqueue executes vdec_esparser_queue_work, could it access the
freed session memory and attempt to take a destroyed mutex?
[ ... ]
> @@ -399,30 +424,13 @@ static void vdec_stop_streaming(struct vb2_queue *q)
[ ... ]
> + if (full_cleanup) {
> + if ((q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
> + !sess->streamon_out) && vdec_codec_needs_recycle(sess)) {
> + kthread_stop(sess->recycle_thread);
> + }
> +
> + vdec_poweroff(sess);
[Severity: High]
If a userspace client starts streaming on both queues and then calls
VIDIOC_STREAMOFF on the CAPTURE queue, the newly added condition evaluating
q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE or !sess->streamon_out
will evaluate to false, skipping kthread_stop.
Since full_cleanup is true, the code proceeds to tear down resources via
vdec_reset_bufs_recycle and kfree of sess->priv.
Could this lead to a use-after-free or list corruption if the recycle thread
continues executing while its backing resources are freed?
> + vdec_free_canvas(sess);
> +
> + if (sess->vififo_vaddr) {
> + dma_free_coherent(sess->core->dev, sess->vififo_size,
> + sess->vififo_vaddr, sess->vififo_paddr);
> + sess->vififo_vaddr = NULL;
> + sess->vififo_paddr = 0;
> + }
> +
> + vdec_reset_timestamps(sess);
[Severity: High]
This is a pre-existing issue, but does vdec_reset_timestamps safely access
the timestamps list here?
The function iterates and frees elements of the sess->timestamps list
without holding ts_spinlock.
If a concurrent threaded ISR executes amvdec_dst_buf_done_idx and calls
amvdec_remove_ts, it will acquire ts_spinlock and attempt to remove an
element.
Could this race condition lead to linked list corruption and use-after-free?
> + vdec_reset_bufs_recycle(sess);
> + core->cur_sess = NULL;
> +
> + kfree(sess->priv);
> + sess->priv = NULL;
> + } else {
> + if (sess->status == STATUS_NEEDS_RESUME)
> + sess->changed_format = 0;
> + }
> + mutex_unlock(&core->lock);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=2
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 02/19] media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown
@ 2026-07-13 12:38 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:38 UTC (permalink / raw)
To: Anand Moon; +Cc: linux-amlogic, neil.armstrong, dri-devel
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [High] The vdec_recycle_thread is conditionally skipped from being stopped but its resources are unconditionally freed during a partial STREAMOFF, leading to Use-After-Free and list corruption.
- [High] Failing start_streaming with -EBUSY permanently leaks streamon flags, causing state inconsistency and cross-session hardware corruption.
Pre-existing issues:
- [High] The return value of `kthread_run()` is not checked, leading to a kernel crash when `kthread_stop()` is called on an `ERR_PTR`.
- [High] vdec_reset_timestamps accesses the timestamps list without holding ts_spinlock, leading to list corruption.
- [High] Missing cancel_work_sync for esparser_queue_work causes UAF and unclocked register access.
--
--- Patch [2]: [PATCH v7 02/19] media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown ---
commit c8f8b26e4a120933b5b1d329b3cc6e97ea21778d
Author: Anand Moon <linux.amoon@gmail.com>
Subject: media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown
This commit addresses race conditions during concurrent STREAMON / STREAMOFF
calls and dynamic resolution changes. It restructures initialization paths,
error handling, and state tracking to prevent hardware state corruption.
Closes: https://lore.kernel.org/all/20260530100841.9CEBA1F00893@smtp.kernel.org/
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
[ ... ]
> @@ -308,9 +303,29 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
> }
>
> if (sess->status == STATUS_RUNNING ||
> - sess->status == STATUS_NEEDS_RESUME ||
> - sess->status == STATUS_INIT)
> + sess->status == STATUS_NEEDS_RESUME)
> return 0;
>
> + /*
> + * Secure the core hardware lock before checking availability
> + * and updating session states to prevent STREAMON race conditions.
> + */
> + mutex_lock(&core->lock);
> + if (core->cur_sess && core->cur_sess != sess) {
> + ret = -EBUSY;
> + mutex_unlock(&core->lock);
> + goto err_unlock_no_hw;
> + }
[Severity: High]
If we jump to err_unlock_no_hw here, doesn't it bypass the
err_cleanup_session label that clears the streamon flags?
If session B calls VIDIOC_STREAMON on its OUTPUT queue and hits this path,
sess->streamon_out will remain 1.
Could a subsequent VIDIOC_QBUF from session B cause vdec_vb2_buf_queue to
schedule esparser_queue_work, potentially overwriting the active hardware
registers of the current session?
[ ... ]
> sess->sequence_cap = 0;
> sess->sequence_out = 0;
> +
> if (vdec_codec_needs_recycle(sess))
> sess->recycle_thread = kthread_run(vdec_recycle_thread, sess,
> "vdec_recycle");
[Severity: High]
This is a pre-existing issue, but does this code properly handle thread
creation failure?
If kthread_run fails due to memory constraints, it returns an ERR_PTR.
Later in vdec_stop_streaming, this pointer is passed unconditionally to
kthread_stop.
Could this cause a kernel panic when kthread_stop attempts to dereference
the ERR_PTR?
> - sess->status = STATUS_INIT;
> - core->cur_sess = sess;
> schedule_work(&sess->esparser_queue_work);
> return 0;
[Severity: High]
This is a pre-existing issue, but does closing the file descriptor safely
clean up the workqueue scheduled here?
When the client queues a buffer and then closes the file descriptor,
vdec_close destroys mutexes and frees the amvdec_session memory via kfree
without cancelling the pending esparser_queue_work.
When the workqueue executes vdec_esparser_queue_work, could it access the
freed session memory and attempt to take a destroyed mutex?
[ ... ]
> @@ -399,30 +424,13 @@ static void vdec_stop_streaming(struct vb2_queue *q)
[ ... ]
> + if (full_cleanup) {
> + if ((q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
> + !sess->streamon_out) && vdec_codec_needs_recycle(sess)) {
> + kthread_stop(sess->recycle_thread);
> + }
> +
> + vdec_poweroff(sess);
[Severity: High]
If a userspace client starts streaming on both queues and then calls
VIDIOC_STREAMOFF on the CAPTURE queue, the newly added condition evaluating
q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE or !sess->streamon_out
will evaluate to false, skipping kthread_stop.
Since full_cleanup is true, the code proceeds to tear down resources via
vdec_reset_bufs_recycle and kfree of sess->priv.
Could this lead to a use-after-free or list corruption if the recycle thread
continues executing while its backing resources are freed?
> + vdec_free_canvas(sess);
> +
> + if (sess->vififo_vaddr) {
> + dma_free_coherent(sess->core->dev, sess->vififo_size,
> + sess->vififo_vaddr, sess->vififo_paddr);
> + sess->vififo_vaddr = NULL;
> + sess->vififo_paddr = 0;
> + }
> +
> + vdec_reset_timestamps(sess);
[Severity: High]
This is a pre-existing issue, but does vdec_reset_timestamps safely access
the timestamps list here?
The function iterates and frees elements of the sess->timestamps list
without holding ts_spinlock.
If a concurrent threaded ISR executes amvdec_dst_buf_done_idx and calls
amvdec_remove_ts, it will acquire ts_spinlock and attempt to remove an
element.
Could this race condition lead to linked list corruption and use-after-free?
> + vdec_reset_bufs_recycle(sess);
> + core->cur_sess = NULL;
> +
> + kfree(sess->priv);
> + sess->priv = NULL;
> + } else {
> + if (sess->status == STATUS_NEEDS_RESUME)
> + sess->changed_format = 0;
> + }
> + mutex_unlock(&core->lock);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=2
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 08/19] media: meson: vdec: Fix concurrent execution races and unsafe teardown
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:42 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:42 UTC (permalink / raw)
To: Anand Moon; +Cc: linux-amlogic, dri-devel, neil.armstrong
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Unconditional nullification of `core->cur_sess` in `vdec_close()` allows concurrent sessions to clobber hardware ownership, triggering interrupt storms and severe memory leaks.
- [High] AB-BA deadlock between `vdec_stop_streaming()` and `esparser_queue_all_src()` workqueue due to `cancel_work_sync()`.
- [High] Inverted loop logic in `esparser_queue_all_src()` causes infinite CPU busy-wait on hardware backpressure and premature exit on success.
- [Medium] Memory leak of `sess->priv` on the newly added `vdec_start_streaming()` error path.
--
--- Patch [8]: [PATCH v7 08/19] media: meson: vdec: Fix concurrent execution races and unsafe teardown ---
commit 4587e6bbd091d6645be624dc185c0fadded8649b
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix concurrent execution races and unsafe teardown
Address data races involving 'should_stop' and prevent multi-session
hardware clobbering by enforcing atomic tracking and strict owner
validation during device teardown.
> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> index e5d3d817b9b2b..959673742e699 100644
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
> @@ -379,7 +379,7 @@ void esparser_queue_all_src(struct work_struct *work)
>
> scoped_guard(mutex, &sess->lock) {
> /* Safe atomic tracking check: exit loop if session is shutting down */
> - if (sess->should_stop)
> + if (READ_ONCE(sess->should_stop))
> return;
[Severity: High]
Could the workqueue get stuck in an infinite loop due to how scoped_guard
expands?
Because scoped_guard(mutex, &sess->lock) expands to a for loop, if the
inner code hits an empty queue or -EAGAIN and calls break, it will only
exit the scoped_guard loop, leaving processed_frame as false.
The code then evaluates if (processed_frame) break; which fails, executes
cond_resched(), and loops the outer while (1) infinitely.
Conversely, if a frame is successfully processed (processed_frame is true),
does the loop break entirely, causing the workqueue to exit after processing
just a single frame?
[ ... ]
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index 6fe9722577179..83a9b1238972a 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
[ ... ]
> @@ -388,10 +392,12 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
> sess->streamon_cap = 0;
>
> mutex_lock(&core->lock);
> - if (core->cur_sess == sess)
> - core->cur_sess = NULL;
> - if (sess->status != STATUS_NEEDS_RESUME)
> - sess->status = STATUS_STOPPED;
> + if (core->cur_sess == sess) {
> + /* Safely clear hardware ownership since we were confirmed as the owner */
> + smp_store_release(&core->cur_sess, NULL);
> + if (sess->status != STATUS_NEEDS_RESUME)
> + sess->status = STATUS_STOPPED;
> + }
> mutex_unlock(&core->lock);
> err_unlock_no_hw:
[Severity: Medium]
Is there a memory leak of sess->priv on the error paths in
vdec_start_streaming()?
If vdec_poweron() succeeds (which dynamically allocates sess->priv), but
a later step like kthread_run() fails, the code jumps to err_poweroff.
While the hardware state and vififo_vaddr are rolled back, kfree(sess->priv)
appears to be missing.
If VIDIOC_STREAMON repeatedly fails, could this leak kernel memory?
[ ... ]
> @@ -493,26 +502,33 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> cancel_work_sync(&sess->esparser_queue_work);
[Severity: High]
Can this cause an AB-BA deadlock between the V4L2 core and the workqueue?
When userspace calls VIDIOC_STREAMOFF, the V4L2 core acquires the queue lock
(sess->lock), then calls vdec_stop_streaming(), which calls
cancel_work_sync() here.
Meanwhile, the esparser_queue_all_src() workqueue loop uses
scoped_guard(mutex, &sess->lock) to acquire the exact same lock. If the
workqueue is active, it will block waiting for sess->lock, while
vdec_stop_streaming() blocks indefinitely waiting for the workqueue to exit.
> mutex_lock(&core->lock);
>
> - vdec_poweroff(sess);
> - vdec_free_canvas(sess);
[ ... ]
> @@ -1012,21 +1031,30 @@ static int vdec_close(struct file *file)
[ ... ]
> kfree(sess->priv);
> sess->priv = NULL;
>
> + /* Unconditionally set our local status to stopped */
> + sess->status = STATUS_STOPPED;
> + /* Safely clear hardware ownership since we were confirmed as the owner */
> + smp_store_release(&core->cur_sess, NULL);
> +
[Severity: Critical]
Does this unconditionally clear the hardware ownership, even if this session
doesn't own it?
If session A is streaming and session B is closed, vdec_close() for session
B will execute smp_store_release(&core->cur_sess, NULL) outside of the
if (core->cur_sess == sess) check.
If core->cur_sess becomes NULL while session A's hardware is active, could
the next hardware interrupt trigger vdec_isr() which sees !core->cur_sess
and returns IRQ_HANDLED without clearing the hardware interrupt status?
Since the interrupt is level-triggered, could this result in an immediate
interrupt storm that hangs the CPU, and permanently leak session A's
vififo_vaddr DMA buffers when session A is eventually closed?
> mutex_unlock(&core->lock);
>
> v4l2_m2m_ctx_release(sess->m2m_ctx);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=8
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 08/19] media: meson: vdec: Fix concurrent execution races and unsafe teardown
@ 2026-07-13 12:42 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:42 UTC (permalink / raw)
To: Anand Moon; +Cc: linux-amlogic, dri-devel, neil.armstrong
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Unconditional nullification of `core->cur_sess` in `vdec_close()` allows concurrent sessions to clobber hardware ownership, triggering interrupt storms and severe memory leaks.
- [High] AB-BA deadlock between `vdec_stop_streaming()` and `esparser_queue_all_src()` workqueue due to `cancel_work_sync()`.
- [High] Inverted loop logic in `esparser_queue_all_src()` causes infinite CPU busy-wait on hardware backpressure and premature exit on success.
- [Medium] Memory leak of `sess->priv` on the newly added `vdec_start_streaming()` error path.
--
--- Patch [8]: [PATCH v7 08/19] media: meson: vdec: Fix concurrent execution races and unsafe teardown ---
commit 4587e6bbd091d6645be624dc185c0fadded8649b
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix concurrent execution races and unsafe teardown
Address data races involving 'should_stop' and prevent multi-session
hardware clobbering by enforcing atomic tracking and strict owner
validation during device teardown.
> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> index e5d3d817b9b2b..959673742e699 100644
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
> @@ -379,7 +379,7 @@ void esparser_queue_all_src(struct work_struct *work)
>
> scoped_guard(mutex, &sess->lock) {
> /* Safe atomic tracking check: exit loop if session is shutting down */
> - if (sess->should_stop)
> + if (READ_ONCE(sess->should_stop))
> return;
[Severity: High]
Could the workqueue get stuck in an infinite loop due to how scoped_guard
expands?
Because scoped_guard(mutex, &sess->lock) expands to a for loop, if the
inner code hits an empty queue or -EAGAIN and calls break, it will only
exit the scoped_guard loop, leaving processed_frame as false.
The code then evaluates if (processed_frame) break; which fails, executes
cond_resched(), and loops the outer while (1) infinitely.
Conversely, if a frame is successfully processed (processed_frame is true),
does the loop break entirely, causing the workqueue to exit after processing
just a single frame?
[ ... ]
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index 6fe9722577179..83a9b1238972a 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
[ ... ]
> @@ -388,10 +392,12 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
> sess->streamon_cap = 0;
>
> mutex_lock(&core->lock);
> - if (core->cur_sess == sess)
> - core->cur_sess = NULL;
> - if (sess->status != STATUS_NEEDS_RESUME)
> - sess->status = STATUS_STOPPED;
> + if (core->cur_sess == sess) {
> + /* Safely clear hardware ownership since we were confirmed as the owner */
> + smp_store_release(&core->cur_sess, NULL);
> + if (sess->status != STATUS_NEEDS_RESUME)
> + sess->status = STATUS_STOPPED;
> + }
> mutex_unlock(&core->lock);
> err_unlock_no_hw:
[Severity: Medium]
Is there a memory leak of sess->priv on the error paths in
vdec_start_streaming()?
If vdec_poweron() succeeds (which dynamically allocates sess->priv), but
a later step like kthread_run() fails, the code jumps to err_poweroff.
While the hardware state and vififo_vaddr are rolled back, kfree(sess->priv)
appears to be missing.
If VIDIOC_STREAMON repeatedly fails, could this leak kernel memory?
[ ... ]
> @@ -493,26 +502,33 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> cancel_work_sync(&sess->esparser_queue_work);
[Severity: High]
Can this cause an AB-BA deadlock between the V4L2 core and the workqueue?
When userspace calls VIDIOC_STREAMOFF, the V4L2 core acquires the queue lock
(sess->lock), then calls vdec_stop_streaming(), which calls
cancel_work_sync() here.
Meanwhile, the esparser_queue_all_src() workqueue loop uses
scoped_guard(mutex, &sess->lock) to acquire the exact same lock. If the
workqueue is active, it will block waiting for sess->lock, while
vdec_stop_streaming() blocks indefinitely waiting for the workqueue to exit.
> mutex_lock(&core->lock);
>
> - vdec_poweroff(sess);
> - vdec_free_canvas(sess);
[ ... ]
> @@ -1012,21 +1031,30 @@ static int vdec_close(struct file *file)
[ ... ]
> kfree(sess->priv);
> sess->priv = NULL;
>
> + /* Unconditionally set our local status to stopped */
> + sess->status = STATUS_STOPPED;
> + /* Safely clear hardware ownership since we were confirmed as the owner */
> + smp_store_release(&core->cur_sess, NULL);
> +
[Severity: Critical]
Does this unconditionally clear the hardware ownership, even if this session
doesn't own it?
If session A is streaming and session B is closed, vdec_close() for session
B will execute smp_store_release(&core->cur_sess, NULL) outside of the
if (core->cur_sess == sess) check.
If core->cur_sess becomes NULL while session A's hardware is active, could
the next hardware interrupt trigger vdec_isr() which sees !core->cur_sess
and returns IRQ_HANDLED without clearing the hardware interrupt status?
Since the interrupt is level-triggered, could this result in an immediate
interrupt storm that hangs the CPU, and permanently leak session A's
vififo_vaddr DMA buffers when session A is eventually closed?
> mutex_unlock(&core->lock);
>
> v4l2_m2m_ctx_release(sess->m2m_ctx);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=8
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 09/19] media: meson: vdec: Fix vp9 header update failure on invalid payloads
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:42 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:42 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, dri-devel, linux-amlogic
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [Critical] Negative error code converted to massive positive integer bypasses validation, leading to out-of-bounds DMA fetch and memory corruption.
- [High] Early return from `esparser_isr` bypasses hardware interrupt clearing, leading to an unrecoverable IRQ storm and system lockup.
- [High] Using `break` inside `scoped_guard` escapes only the guard mechanism, causing an infinite busy-wait loop when the worker queue is empty.
Pre-existing issues:
- [Critical] Implicit integer promotion of VP9 frame sizes allows crafting negative sizes to bypass bounds checks, causing a massive out-of-bounds heap copy.
- [High] esparser worker accesses hardware registers and waitqueues without taking the global lock, causing hardware state corruption across concurrent sessions.
- [High] VP9 frame size validation checks against maximum buffer capacity instead of actual payload size, allowing information disclosure of uninitialized kernel memory.
--
--- Patch [9]: [PATCH v7 09/19] media: meson: vdec: Fix vp9 header update failure on invalid payloads ---
commit e42c61ef1ea850aa0156d3301824f682854a42da
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix vp9 header update failure on invalid payloads
Ensure vp9_update_header() returns an explicit error code on invalid or
malformed buffer payloads instead of silently returning zero.
> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
> @@ -97,11 +97,15 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
> unsigned char *old_header = NULL;
>
> dp = (uint8_t *)vb2_plane_vaddr(buf, 0);
> + if (!dp)
> + return -EINVAL;
[Severity: Critical]
Does returning a negative error code here cause an issue in the caller?
Looking at esparser_queue(), it assigns the return value of
vp9_update_header() to payload_size and checks if (payload_size == 0). If
this returns -EINVAL and the caller assigns the result to a u32 variable,
could the negative error code underflow to a massive positive integer (like
4294967274)?
This might completely bypass validation and be passed to
esparser_pad_start_code(), causing an out-of-bounds memset and out-of-bounds
DMA commands to the hardware.
> dsize = vb2_get_plane_payload(buf, 0);
>
> - if (dsize == vb2_plane_size(buf, 0)) {
> - dev_warn(core->dev, "%s: unable to update header\n", __func__);
> - return 0;
> + if (dsize <= 0 || dsize > vb2_plane_size(buf, 0)) {
> + dev_warn(core->dev, "%s: invalid payload size %d\n",
> + __func__, dsize);
> + return -EINVAL;
> }
>
[ ... ]
> @@ -109,13 +113,16 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
> num_frames = (marker & 0x7) + 1;
> mag = ((marker >> 3) & 0x3) + 1;
> mag_ptr = dsize - mag * num_frames - 2;
> - if (dp[mag_ptr] != marker)
> - return 0;
> + if (mag_ptr < 0 || dp[mag_ptr] != marker)
> + return -EINVAL;
>
> mag_ptr++;
> for (cur_frame = 0; cur_frame < num_frames; cur_frame++) {
> frame_size[cur_frame] = 0;
> for (cur_mag = 0; cur_mag < mag; cur_mag++) {
> + if (mag_ptr >= dsize)
> + return -EINVAL;
> +
> frame_size[cur_frame] |=
> (dp[mag_ptr] << (cur_mag * 8));
[Severity: Critical]
This isn't a bug introduced by this patch, but does this shift operation
cause an implicit sign extension?
Because dp is a pointer to u8, the byte at dp[mag_ptr] undergoes integer
promotion to a signed int before shifting. If cur_mag is 3 and the byte is
128 or larger, shifting it by 24 bits produces a negative value due to the
sign bit.
Could this allow crafting combinations of large positive and negative
frame_size values so their sum wraps around to a small positive integer,
bypassing the bounds check later on and causing a massive out-of-bounds
memmove()?
> mag_ptr++;
> }
[ ... ]
> @@ -140,7 +147,7 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
>
> if (new_frame_size >= vb2_plane_size(buf, 0)) {
> dev_warn(core->dev, "%s: unable to update header\n", __func__);
> - return 0;
> + return -ENOMEM;
> }
[Severity: High]
This isn't a bug introduced by this patch, but does validating against the
total buffer capacity instead of the actual payload size allow information
disclosure?
If the check uses vb2_plane_size(buf, 0) instead of the actual received
payload length in dsize, could a valid short payload with a crafted
superframe index declare frame sizes that exceed the payload length but
remain under the total buffer capacity?
If so, memmove() could copy trailing uninitialized or stale kernel memory
from the buffer into the active payload stream.
Outside of the immediate diff, there are a few other questions about the
esparser implementation that may be worth reviewing:
[Severity: High]
This isn't a bug introduced by this patch, but does esparser_queue_all_src()
queue hardware operations without acquiring the global hardware lock?
The worker runs concurrently for multiple sessions because the work queue is
initialized per-session. The worker locks the per-session sess->lock but
does not appear to acquire the global core->lock or verify hardware ownership
before calling esparser_write_data():
esparser_queue_all_src() {
...
ret = esparser_queue(sess, vbuf);
...
}
Could multiple sessions invoke esparser_write_data() simultaneously, causing
conflicting writes to the hardware parser registers and corruption of the
global search_done waitqueue flag?
[Severity: High]
Could the early return in esparser_isr() bypass clearing the hardware
interrupt?
The interrupt service routine starts with a check:
esparser_isr() {
if (!sess)
return IRQ_HANDLED;
...
}
If a hardware interrupt asserts just as the session is torn down, returning
early leaves the hardware interrupt un-cleared. Would this cause the
hardware to hold the interrupt line asserted indefinitely, leading to an
unrecoverable IRQ storm and system lockup?
[Severity: High]
Does the break statement inside esparser_queue_all_src() properly exit the
worker loop?
When checking if a buffer is available inside a scoped_guard():
esparser_queue_all_src() {
...
vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
if (!vbuf)
break;
...
}
Since scoped_guard() is implemented internally as a for loop, breaking here
would only escape the guard's inner loop. If processed_frame remains false,
the outer while (1) loop proceeds past its own exit condition:
if (processed_frame)
break;
/* Give other threads and IRQ routines a window ... */
cond_resched();
Could this pin a CPU core at 100% in an infinite busy-wait loop when the
queue is empty?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=9
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 09/19] media: meson: vdec: Fix vp9 header update failure on invalid payloads
@ 2026-07-13 12:42 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:42 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, dri-devel, linux-amlogic
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [Critical] Negative error code converted to massive positive integer bypasses validation, leading to out-of-bounds DMA fetch and memory corruption.
- [High] Early return from `esparser_isr` bypasses hardware interrupt clearing, leading to an unrecoverable IRQ storm and system lockup.
- [High] Using `break` inside `scoped_guard` escapes only the guard mechanism, causing an infinite busy-wait loop when the worker queue is empty.
Pre-existing issues:
- [Critical] Implicit integer promotion of VP9 frame sizes allows crafting negative sizes to bypass bounds checks, causing a massive out-of-bounds heap copy.
- [High] esparser worker accesses hardware registers and waitqueues without taking the global lock, causing hardware state corruption across concurrent sessions.
- [High] VP9 frame size validation checks against maximum buffer capacity instead of actual payload size, allowing information disclosure of uninitialized kernel memory.
--
--- Patch [9]: [PATCH v7 09/19] media: meson: vdec: Fix vp9 header update failure on invalid payloads ---
commit e42c61ef1ea850aa0156d3301824f682854a42da
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix vp9 header update failure on invalid payloads
Ensure vp9_update_header() returns an explicit error code on invalid or
malformed buffer payloads instead of silently returning zero.
> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
> @@ -97,11 +97,15 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
> unsigned char *old_header = NULL;
>
> dp = (uint8_t *)vb2_plane_vaddr(buf, 0);
> + if (!dp)
> + return -EINVAL;
[Severity: Critical]
Does returning a negative error code here cause an issue in the caller?
Looking at esparser_queue(), it assigns the return value of
vp9_update_header() to payload_size and checks if (payload_size == 0). If
this returns -EINVAL and the caller assigns the result to a u32 variable,
could the negative error code underflow to a massive positive integer (like
4294967274)?
This might completely bypass validation and be passed to
esparser_pad_start_code(), causing an out-of-bounds memset and out-of-bounds
DMA commands to the hardware.
> dsize = vb2_get_plane_payload(buf, 0);
>
> - if (dsize == vb2_plane_size(buf, 0)) {
> - dev_warn(core->dev, "%s: unable to update header\n", __func__);
> - return 0;
> + if (dsize <= 0 || dsize > vb2_plane_size(buf, 0)) {
> + dev_warn(core->dev, "%s: invalid payload size %d\n",
> + __func__, dsize);
> + return -EINVAL;
> }
>
[ ... ]
> @@ -109,13 +113,16 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
> num_frames = (marker & 0x7) + 1;
> mag = ((marker >> 3) & 0x3) + 1;
> mag_ptr = dsize - mag * num_frames - 2;
> - if (dp[mag_ptr] != marker)
> - return 0;
> + if (mag_ptr < 0 || dp[mag_ptr] != marker)
> + return -EINVAL;
>
> mag_ptr++;
> for (cur_frame = 0; cur_frame < num_frames; cur_frame++) {
> frame_size[cur_frame] = 0;
> for (cur_mag = 0; cur_mag < mag; cur_mag++) {
> + if (mag_ptr >= dsize)
> + return -EINVAL;
> +
> frame_size[cur_frame] |=
> (dp[mag_ptr] << (cur_mag * 8));
[Severity: Critical]
This isn't a bug introduced by this patch, but does this shift operation
cause an implicit sign extension?
Because dp is a pointer to u8, the byte at dp[mag_ptr] undergoes integer
promotion to a signed int before shifting. If cur_mag is 3 and the byte is
128 or larger, shifting it by 24 bits produces a negative value due to the
sign bit.
Could this allow crafting combinations of large positive and negative
frame_size values so their sum wraps around to a small positive integer,
bypassing the bounds check later on and causing a massive out-of-bounds
memmove()?
> mag_ptr++;
> }
[ ... ]
> @@ -140,7 +147,7 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
>
> if (new_frame_size >= vb2_plane_size(buf, 0)) {
> dev_warn(core->dev, "%s: unable to update header\n", __func__);
> - return 0;
> + return -ENOMEM;
> }
[Severity: High]
This isn't a bug introduced by this patch, but does validating against the
total buffer capacity instead of the actual payload size allow information
disclosure?
If the check uses vb2_plane_size(buf, 0) instead of the actual received
payload length in dsize, could a valid short payload with a crafted
superframe index declare frame sizes that exceed the payload length but
remain under the total buffer capacity?
If so, memmove() could copy trailing uninitialized or stale kernel memory
from the buffer into the active payload stream.
Outside of the immediate diff, there are a few other questions about the
esparser implementation that may be worth reviewing:
[Severity: High]
This isn't a bug introduced by this patch, but does esparser_queue_all_src()
queue hardware operations without acquiring the global hardware lock?
The worker runs concurrently for multiple sessions because the work queue is
initialized per-session. The worker locks the per-session sess->lock but
does not appear to acquire the global core->lock or verify hardware ownership
before calling esparser_write_data():
esparser_queue_all_src() {
...
ret = esparser_queue(sess, vbuf);
...
}
Could multiple sessions invoke esparser_write_data() simultaneously, causing
conflicting writes to the hardware parser registers and corruption of the
global search_done waitqueue flag?
[Severity: High]
Could the early return in esparser_isr() bypass clearing the hardware
interrupt?
The interrupt service routine starts with a check:
esparser_isr() {
if (!sess)
return IRQ_HANDLED;
...
}
If a hardware interrupt asserts just as the session is torn down, returning
early leaves the hardware interrupt un-cleared. Would this cause the
hardware to hold the interrupt line asserted indefinitely, leading to an
unrecoverable IRQ storm and system lockup?
[Severity: High]
Does the break statement inside esparser_queue_all_src() properly exit the
worker loop?
When checking if a buffer is available inside a scoped_guard():
esparser_queue_all_src() {
...
vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
if (!vbuf)
break;
...
}
Since scoped_guard() is implemented internally as a for loop, breaking here
would only escape the guard's inner loop. If processed_frame remains false,
the outer while (1) loop proceeds past its own exit condition:
if (processed_frame)
break;
/* Give other threads and IRQ routines a window ... */
cond_resched();
Could this pin a CPU core at 100% in an infinite busy-wait loop when the
queue is empty?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=9
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 10/19] media: meson: vdec: Fix race conditions and leaks in esparser pipeline
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:46 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:46 UTC (permalink / raw)
To: Anand Moon; +Cc: dri-devel, neil.armstrong, linux-amlogic
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
New issues:
- [High] Infinite loop and CPU soft-lockup in `esparser_queue_all_src` due to inverted `processed_frame` logic.
- [High] Self-deadlock in `vdec_start_streaming` and `vdec_stop_streaming` due to recursive acquisition of `core->lock`.
- [High] AB-BA deadlock in `vdec_stop_streaming` caused by calling `cancel_work_sync` while holding `sess->lock`.
- [High] AB-BA lock inversion deadlock between `core->lock` and `sess->lock` in the esparser workqueue.
- [High] System-wide V4L2 stall due to sleeping for up to 200ms while holding the driver-wide `core->lock`.
- [High] Silent omission of start code padding for unmapped buffers leads to hardware out-of-bounds DMA.
Pre-existing issues:
- [Critical] Integer overflow in `vp9_update_header` allows massive out-of-bounds heap corruption from untrusted payload data.
- [High] `esparser_pad_start_code` executes an out-of-bounds memory write before evaluating bounds checks.
--
--- Patch [10]: [PATCH v7 10/19] media: meson: vdec: Fix race conditions and leaks in esparser pipeline ---
commit f978165e2ef967ec3f207c80310d474efee3c9e8
Author: Anand Moon <linux.amoon@gmail.com>
Subject: media: meson: vdec: Fix race conditions and leaks in esparser pipeline
This commit fixes a circular locking dependency and potential race conditions
within the asynchronous esparser queue. It moves hardware ownership validation
inside the core->lock mutex region and properly cleans up timestamp trackers.
The VP9 payload evaluation logic is also refined.
> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> index edbfc829e2da8..b9f36fef4be12 100644
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
> @@ -199,12 +199,16 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
> * the ESPARSER interrupt.
> */
> static u32 esparser_pad_start_code(struct amvdec_core *core,
> + struct amvdec_session *sess,
> struct vb2_buffer *vb,
> u32 payload_size)
> {
> u32 pad_size = 0;
> u8 *vaddr = vb2_plane_vaddr(vb, 0);
>
> + if (!sess || READ_ONCE(sess->should_stop) || !sess->priv || !vaddr)
> + return 0;
[Severity: High]
Does returning 0 here skip the start code injection and cause an
out-of-bounds DMA scan?
By bypassing the injection of the SEARCH_PATTERN start code, the caller
esparser_queue() might interpret the 0 as success and instruct the DMA
engine to fetch the buffer.
Since the search pattern is missing, could the hardware parser scan
indefinitely past the valid buffer into adjacent physical memory, resulting
in an out-of-bounds DMA read?
> if (payload_size < ESPARSER_MIN_PACKET_SIZE) {
> pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size;
> memset(vaddr + payload_size, 0, pad_size);
> }
[Severity: High]
This is a pre-existing issue, but does this memset() write out of bounds?
If a small buffer is queued, this calculation executes before the
overall bounds check is evaluated.
Could this overwrite adjacent kernel memory with zeroes before validating
the size?
[ ... ]
> @@ -349,24 +356,38 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
> vbuf->sequence = sess->sequence_out++;
>
> if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
> - payload_size = vp9_update_header(core, vb);
> + int res = vp9_update_header(core, vb);
[Severity: Critical]
This is a pre-existing issue, but does vp9_update_header() have an integer
overflow?
By extracting untrusted payload bytes and shifting them left up to 24 bits
into a signed integer, can malicious payloads construct negative frame sizes?
If a highly negative signed integer is produced and passed to memmove() as an
unsigned size_t, could this cause massive out-of-bounds reads and writes on
the kernel heap or DMA memory?
>
> - if (payload_size == 0) {
> - dev_err(core->dev, "esparser: VP9 header update failed\n");
> + if (res <= 0) {
> + dev_err(core->dev,
> + "esparser: VP9 header update failed (%d)\n",
> + res);
> amvdec_remove_ts(sess, vb->timestamp);
> return -EBADMSG;
> }
> + payload_size = res;
> + }
> +
> + pad_size = esparser_pad_start_code(core, sess, vb, payload_size);
> +
> + /* Protect hardware register writes under core->lock */
> + mutex_lock(&core->lock);
[Severity: High]
Does this new mutex_lock() introduce an AB-BA lock inversion?
The background workqueue thread esparser_queue_all_src() acquires sess->lock
first (via scoped_guard) and then calls esparser_queue().
By adding mutex_lock(&core->lock) here, could this conflict with V4L2 ioctls
that implicitly acquire core->lock before sess->lock, resulting in an AB-BA
deadlock?
> + if (core->cur_sess != sess || READ_ONCE(sess->should_stop)) {
> + mutex_unlock(&core->lock);
> + amvdec_remove_ts(sess, vb->timestamp);
> + return -ESHUTDOWN;
> }
>
> - pad_size = esparser_pad_start_code(core, vb, payload_size);
> ret = esparser_write_data(core, phy, payload_size + pad_size);
[Severity: High]
Could sleeping inside esparser_write_data() block all other V4L2 ioctls?
Because core->lock is mapped to vdev->lock, sleeping via
wait_event_interruptible_timeout() while waiting for the hardware interrupt
blocks all other V4L2 ioctls across the entire device for the duration of
the fetch.
Can this severely degrade system performance?
>
> if (ret <= 0) {
> dev_warn(core->dev, "esparser: input parsing error\n");
> amvdec_remove_ts(sess, vb->timestamp);
> amvdec_write_parser(core, PARSER_FETCH_CMD, 0);
> + mutex_unlock(&core->lock);
> return -EIO;
> }
> + mutex_unlock(&core->lock);
>
> atomic_inc(&sess->esparser_queued_bufs);
>
I also have some questions about related code paths outside the diff:
[Severity: High]
Is there an infinite loop in esparser_queue_all_src()?
When looking at the worker thread:
drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src() {
...
bool processed_frame = false;
...
if (processed_frame)
break;
...
}
Because the break statements inside the scoped_guard macro only exit the
internal macro loop, processed_frame remains false when the queue is empty.
Does the inverted logic cause the thread to bypass the outer break and loop
infinitely, soft-locking the CPU?
[Severity: High]
Do vdec_start_streaming() and vdec_stop_streaming() self-deadlock?
In the current code:
drivers/staging/media/meson/vdec/vdec.c:vdec_start_streaming() {
...
mutex_lock(&core->lock);
...
}
Because the device's vdev->lock is mapped directly to &core->lock, the V4L2
core framework automatically acquires it before dispatching standard ioctls.
Does explicitly calling mutex_lock(&core->lock) inside these callbacks
result in a permanent self-deadlock?
[Severity: High]
Is there an AB-BA deadlock during stream teardown?
During streamoff:
drivers/staging/media/meson/vdec/vdec.c:vdec_stop_streaming() {
...
cancel_work_sync(&sess->esparser_queue_work);
...
}
The ioctl thread acquires sess->lock before invoking this callback.
Concurrently, the background workqueue thread esparser_queue_all_src()
blocks waiting for sess->lock.
Does calling cancel_work_sync() here cause the ioctl thread to block forever
waiting for the worker thread, resulting in an AB-BA deadlock?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=10
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 10/19] media: meson: vdec: Fix race conditions and leaks in esparser pipeline
@ 2026-07-13 12:46 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:46 UTC (permalink / raw)
To: Anand Moon; +Cc: dri-devel, neil.armstrong, linux-amlogic
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
New issues:
- [High] Infinite loop and CPU soft-lockup in `esparser_queue_all_src` due to inverted `processed_frame` logic.
- [High] Self-deadlock in `vdec_start_streaming` and `vdec_stop_streaming` due to recursive acquisition of `core->lock`.
- [High] AB-BA deadlock in `vdec_stop_streaming` caused by calling `cancel_work_sync` while holding `sess->lock`.
- [High] AB-BA lock inversion deadlock between `core->lock` and `sess->lock` in the esparser workqueue.
- [High] System-wide V4L2 stall due to sleeping for up to 200ms while holding the driver-wide `core->lock`.
- [High] Silent omission of start code padding for unmapped buffers leads to hardware out-of-bounds DMA.
Pre-existing issues:
- [Critical] Integer overflow in `vp9_update_header` allows massive out-of-bounds heap corruption from untrusted payload data.
- [High] `esparser_pad_start_code` executes an out-of-bounds memory write before evaluating bounds checks.
--
--- Patch [10]: [PATCH v7 10/19] media: meson: vdec: Fix race conditions and leaks in esparser pipeline ---
commit f978165e2ef967ec3f207c80310d474efee3c9e8
Author: Anand Moon <linux.amoon@gmail.com>
Subject: media: meson: vdec: Fix race conditions and leaks in esparser pipeline
This commit fixes a circular locking dependency and potential race conditions
within the asynchronous esparser queue. It moves hardware ownership validation
inside the core->lock mutex region and properly cleans up timestamp trackers.
The VP9 payload evaluation logic is also refined.
> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> index edbfc829e2da8..b9f36fef4be12 100644
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
> @@ -199,12 +199,16 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
> * the ESPARSER interrupt.
> */
> static u32 esparser_pad_start_code(struct amvdec_core *core,
> + struct amvdec_session *sess,
> struct vb2_buffer *vb,
> u32 payload_size)
> {
> u32 pad_size = 0;
> u8 *vaddr = vb2_plane_vaddr(vb, 0);
>
> + if (!sess || READ_ONCE(sess->should_stop) || !sess->priv || !vaddr)
> + return 0;
[Severity: High]
Does returning 0 here skip the start code injection and cause an
out-of-bounds DMA scan?
By bypassing the injection of the SEARCH_PATTERN start code, the caller
esparser_queue() might interpret the 0 as success and instruct the DMA
engine to fetch the buffer.
Since the search pattern is missing, could the hardware parser scan
indefinitely past the valid buffer into adjacent physical memory, resulting
in an out-of-bounds DMA read?
> if (payload_size < ESPARSER_MIN_PACKET_SIZE) {
> pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size;
> memset(vaddr + payload_size, 0, pad_size);
> }
[Severity: High]
This is a pre-existing issue, but does this memset() write out of bounds?
If a small buffer is queued, this calculation executes before the
overall bounds check is evaluated.
Could this overwrite adjacent kernel memory with zeroes before validating
the size?
[ ... ]
> @@ -349,24 +356,38 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
> vbuf->sequence = sess->sequence_out++;
>
> if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
> - payload_size = vp9_update_header(core, vb);
> + int res = vp9_update_header(core, vb);
[Severity: Critical]
This is a pre-existing issue, but does vp9_update_header() have an integer
overflow?
By extracting untrusted payload bytes and shifting them left up to 24 bits
into a signed integer, can malicious payloads construct negative frame sizes?
If a highly negative signed integer is produced and passed to memmove() as an
unsigned size_t, could this cause massive out-of-bounds reads and writes on
the kernel heap or DMA memory?
>
> - if (payload_size == 0) {
> - dev_err(core->dev, "esparser: VP9 header update failed\n");
> + if (res <= 0) {
> + dev_err(core->dev,
> + "esparser: VP9 header update failed (%d)\n",
> + res);
> amvdec_remove_ts(sess, vb->timestamp);
> return -EBADMSG;
> }
> + payload_size = res;
> + }
> +
> + pad_size = esparser_pad_start_code(core, sess, vb, payload_size);
> +
> + /* Protect hardware register writes under core->lock */
> + mutex_lock(&core->lock);
[Severity: High]
Does this new mutex_lock() introduce an AB-BA lock inversion?
The background workqueue thread esparser_queue_all_src() acquires sess->lock
first (via scoped_guard) and then calls esparser_queue().
By adding mutex_lock(&core->lock) here, could this conflict with V4L2 ioctls
that implicitly acquire core->lock before sess->lock, resulting in an AB-BA
deadlock?
> + if (core->cur_sess != sess || READ_ONCE(sess->should_stop)) {
> + mutex_unlock(&core->lock);
> + amvdec_remove_ts(sess, vb->timestamp);
> + return -ESHUTDOWN;
> }
>
> - pad_size = esparser_pad_start_code(core, vb, payload_size);
> ret = esparser_write_data(core, phy, payload_size + pad_size);
[Severity: High]
Could sleeping inside esparser_write_data() block all other V4L2 ioctls?
Because core->lock is mapped to vdev->lock, sleeping via
wait_event_interruptible_timeout() while waiting for the hardware interrupt
blocks all other V4L2 ioctls across the entire device for the duration of
the fetch.
Can this severely degrade system performance?
>
> if (ret <= 0) {
> dev_warn(core->dev, "esparser: input parsing error\n");
> amvdec_remove_ts(sess, vb->timestamp);
> amvdec_write_parser(core, PARSER_FETCH_CMD, 0);
> + mutex_unlock(&core->lock);
> return -EIO;
> }
> + mutex_unlock(&core->lock);
>
> atomic_inc(&sess->esparser_queued_bufs);
>
I also have some questions about related code paths outside the diff:
[Severity: High]
Is there an infinite loop in esparser_queue_all_src()?
When looking at the worker thread:
drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src() {
...
bool processed_frame = false;
...
if (processed_frame)
break;
...
}
Because the break statements inside the scoped_guard macro only exit the
internal macro loop, processed_frame remains false when the queue is empty.
Does the inverted logic cause the thread to bypass the outer break and loop
infinitely, soft-locking the CPU?
[Severity: High]
Do vdec_start_streaming() and vdec_stop_streaming() self-deadlock?
In the current code:
drivers/staging/media/meson/vdec/vdec.c:vdec_start_streaming() {
...
mutex_lock(&core->lock);
...
}
Because the device's vdev->lock is mapped directly to &core->lock, the V4L2
core framework automatically acquires it before dispatching standard ioctls.
Does explicitly calling mutex_lock(&core->lock) inside these callbacks
result in a permanent self-deadlock?
[Severity: High]
Is there an AB-BA deadlock during stream teardown?
During streamoff:
drivers/staging/media/meson/vdec/vdec.c:vdec_stop_streaming() {
...
cancel_work_sync(&sess->esparser_queue_work);
...
}
The ioctl thread acquires sess->lock before invoking this callback.
Concurrently, the background workqueue thread esparser_queue_all_src()
blocks waiting for sess->lock.
Does calling cancel_work_sync() here cause the ioctl thread to block forever
waiting for the worker thread, resulting in an AB-BA deadlock?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=10
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 11/19] media: meson: vdec: Update core m2m stream state during transitions
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:48 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:48 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, dri-devel, linux-amlogic
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] The error path in `vdec_start_streaming` flushes BOTH M2M source and destination buffer queues, causing buffers of an already-streaming queue to be silently stranded.
- [High] The teardown sequence in `vdec_stop_streaming` calls `synchronize_irq()` before stopping the hardware, leaving a race window for a Use-After-Free in the interrupt handler.
- [High] Active DMA buffers are returned to the videobuf2 framework before the hardware is stopped, potentially causing memory corruption or IOMMU faults.
--
--- Patch [11]: [PATCH v7 11/19] media: meson: vdec: Update core m2m stream state during transitions ---
commit 8309f1636cfcc48cc67621545812af232aebc2dd
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Update core m2m stream state during transitions
Explicitly update the V4L2 core Memory-to-Memory (m2m) framework's
internal streaming states when initiating or terminating streaming on
the device queues.
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -299,6 +299,8 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
> else
> sess->streamon_cap = 1;
>
> + v4l2_m2m_update_start_streaming_state(sess->m2m_ctx, q);
> +
> if (!sess->streamon_out)
> return 0;
[Severity: High]
This isn't a bug introduced by this patch, but does the error path in
vdec_start_streaming() mistakenly strand buffers if one queue fails to start?
If the CAPTURE queue starts successfully, but the OUTPUT queue fails to start
due to the hardware being busy, the error path flushes both queues:
drivers/staging/media/meson/vdec/vdec.c:vdec_start_streaming() {
...
err_unlock_no_hw:
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
...
}
Because the CAPTURE queue is actively streaming, wouldn't returning these
buffers with VB2_BUF_STATE_QUEUED cause the vb2 core to leave them in the
queued list without putting them on the done list, permanently freezing
the userspace application?
[ ... ]
> @@ -529,6 +531,8 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> sess->changed_format = 0;
> }
>
> + v4l2_m2m_update_stop_streaming_state(sess->m2m_ctx, q);
> +
> mutex_unlock(&core->lock);
> }
[Severity: High]
This is a pre-existing issue, but does vdec_stop_streaming() return active
DMA buffers to the videobuf2 framework before the hardware is stopped?
When flushing buffers, it immediately returns them to the V4L2 core:
drivers/staging/media/meson/vdec/vdec.c:vdec_stop_streaming() {
...
/* flush buffers to kill background workqueue thread */
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
sess->streamon_out = 0;
} else {
...
}
If the vb2 framework unmaps these buffers while the hardware is still running
and writing to physical memory, can this cause DMA memory corruption or IOMMU
faults before vdec_poweroff() is finally called later in the function?
[Severity: High]
This is a pre-existing issue, but does the teardown sequence in
vdec_stop_streaming() leave a race window for a use-after-free?
The synchronization happens before the hardware is halted:
drivers/staging/media/meson/vdec/vdec.c:vdec_stop_streaming() {
...
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
/* Ensure esparser ISR finishes executing */
synchronize_irq(core->esparser_irq);
cancel_work_sync(&sess->esparser_queue_work);
mutex_lock(&core->lock);
if (core->cur_sess == sess) {
vdec_poweroff(sess);
...
kfree(sess->priv);
...
}
If a new hardware interrupt fires immediately after the synchronization
completes, and vdec_stop_streaming() proceeds to free sess->priv,
could the concurrent ISR access the freed session data?
drivers/staging/media/meson/vdec/vdec.c:vdec_isr() {
...
/* Secure an atomic acquire snapshot to protect against concurrent teardown */
sess = smp_load_acquire(&core->cur_sess);
if (!sess)
return IRQ_HANDLED;
sess->last_irq_jiffies = get_jiffies_64();
return sess->fmt_out->codec_ops->isr(sess);
...
}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=11
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 11/19] media: meson: vdec: Update core m2m stream state during transitions
@ 2026-07-13 12:48 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:48 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, dri-devel, linux-amlogic
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] The error path in `vdec_start_streaming` flushes BOTH M2M source and destination buffer queues, causing buffers of an already-streaming queue to be silently stranded.
- [High] The teardown sequence in `vdec_stop_streaming` calls `synchronize_irq()` before stopping the hardware, leaving a race window for a Use-After-Free in the interrupt handler.
- [High] Active DMA buffers are returned to the videobuf2 framework before the hardware is stopped, potentially causing memory corruption or IOMMU faults.
--
--- Patch [11]: [PATCH v7 11/19] media: meson: vdec: Update core m2m stream state during transitions ---
commit 8309f1636cfcc48cc67621545812af232aebc2dd
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Update core m2m stream state during transitions
Explicitly update the V4L2 core Memory-to-Memory (m2m) framework's
internal streaming states when initiating or terminating streaming on
the device queues.
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -299,6 +299,8 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
> else
> sess->streamon_cap = 1;
>
> + v4l2_m2m_update_start_streaming_state(sess->m2m_ctx, q);
> +
> if (!sess->streamon_out)
> return 0;
[Severity: High]
This isn't a bug introduced by this patch, but does the error path in
vdec_start_streaming() mistakenly strand buffers if one queue fails to start?
If the CAPTURE queue starts successfully, but the OUTPUT queue fails to start
due to the hardware being busy, the error path flushes both queues:
drivers/staging/media/meson/vdec/vdec.c:vdec_start_streaming() {
...
err_unlock_no_hw:
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
...
}
Because the CAPTURE queue is actively streaming, wouldn't returning these
buffers with VB2_BUF_STATE_QUEUED cause the vb2 core to leave them in the
queued list without putting them on the done list, permanently freezing
the userspace application?
[ ... ]
> @@ -529,6 +531,8 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> sess->changed_format = 0;
> }
>
> + v4l2_m2m_update_stop_streaming_state(sess->m2m_ctx, q);
> +
> mutex_unlock(&core->lock);
> }
[Severity: High]
This is a pre-existing issue, but does vdec_stop_streaming() return active
DMA buffers to the videobuf2 framework before the hardware is stopped?
When flushing buffers, it immediately returns them to the V4L2 core:
drivers/staging/media/meson/vdec/vdec.c:vdec_stop_streaming() {
...
/* flush buffers to kill background workqueue thread */
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
sess->streamon_out = 0;
} else {
...
}
If the vb2 framework unmaps these buffers while the hardware is still running
and writing to physical memory, can this cause DMA memory corruption or IOMMU
faults before vdec_poweroff() is finally called later in the function?
[Severity: High]
This is a pre-existing issue, but does the teardown sequence in
vdec_stop_streaming() leave a race window for a use-after-free?
The synchronization happens before the hardware is halted:
drivers/staging/media/meson/vdec/vdec.c:vdec_stop_streaming() {
...
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
/* Ensure esparser ISR finishes executing */
synchronize_irq(core->esparser_irq);
cancel_work_sync(&sess->esparser_queue_work);
mutex_lock(&core->lock);
if (core->cur_sess == sess) {
vdec_poweroff(sess);
...
kfree(sess->priv);
...
}
If a new hardware interrupt fires immediately after the synchronization
completes, and vdec_stop_streaming() proceeds to free sess->priv,
could the concurrent ISR access the freed session data?
drivers/staging/media/meson/vdec/vdec.c:vdec_isr() {
...
/* Secure an atomic acquire snapshot to protect against concurrent teardown */
sess = smp_load_acquire(&core->cur_sess);
if (!sess)
return IRQ_HANDLED;
sess->last_irq_jiffies = get_jiffies_64();
return sess->fmt_out->codec_ops->isr(sess);
...
}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=11
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 14/19] media: meson: vdec: Correct atomic counter placement in dst_buf_done
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:48 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:48 UTC (permalink / raw)
To: Anand Moon; +Cc: dri-devel, linux-amlogic, neil.armstrong
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [Critical] The patch introduces an unhandled double decrement of `sess->esparser_queued_bufs`, causing the atomic counter to underflow, corrupting stream state evaluation, and deadlocking the driver.
- [High] Using `break` inside `scoped_guard` fails to exit the worker loop, resulting in a 100% CPU infinite loop deadlock.
- [High] Returning 0 on `!vaddr` in `esparser_pad_start_code()` skips writing the mandatory start code pattern, leading to hardware out-of-bounds reads.
- [Medium] The commit message erroneously claims to relocate a decrement from the 'entry point' of the function, presenting a false premise for the change.
Pre-existing issues:
- [High] Missing decrement of `esparser_queued_bufs` on error paths permanently leaks logical buffer slots.
--
--- Patch [14]: [PATCH v7 14/19] media: meson: vdec: Correct atomic counter placement in dst_buf_done ---
commit 3bacd92d37472439ff6f7550b68eb503f9d760f0
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Correct atomic counter placement in dst_buf_done
This commit fixes an end-of-stream boundary condition by relocating
the atomic decrement helper to execute after state evaluation. It
aims to ensure accurate stream termination signaling.
> Previously, decrementing sess->esparser_queued_bufs at the entry point
> of the function corrupted the conditional checks for the final video
> frame.
[Severity: Medium]
Is this description accurate? Looking at the baseline code, dst_buf_done()
never contained an atomic decrement at its entry point or anywhere else
inside the function. The decrement only existed in the caller functions.
> diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.c b/drivers/staging/media/meson/vdec/vdec_helpers.c
> index f02c21d5a9c18..303236f0647c0 100644
> --- a/drivers/staging/media/meson/vdec/vdec_helpers.c
> +++ b/drivers/staging/media/meson/vdec/vdec_helpers.c
> @@ -314,6 +314,9 @@ static void dst_buf_done(struct amvdec_session *sess,
> dev_dbg(dev, "Buffer %u done, ts = %llu, flags = %08X\n",
> vbuf->vb2_buf.index, timestamp, flags);
> vbuf->field = field;
> +
> + atomic_dec(&sess->esparser_queued_bufs);
> +
[Severity: Critical]
Does this introduce a double decrement? Since the callers like
amvdec_dst_buf_done() and amvdec_dst_buf_done_offset() still contain their
original atomic_dec() calls, this new decrement causes the counter to
underflow.
When the counter becomes negative, the EOS condition
atomic_read(&sess->esparser_queued_bufs) <= 1 evaluates to true, which
can falsely trigger V4L2_EVENT_EOS on the wrong buffer and cause a
permanent -EAGAIN deadlock.
> v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
>
> /* Buffer done probably means the vififo got freed */
[Severity: High]
This is a pre-existing issue, but does the error path in amvdec_dst_buf_done()
permanently leak logical buffer slots?
drivers/staging/media/meson/vdec/vdec_helpers.c:amvdec_dst_buf_done() {
...
if (list_empty(&sess->timestamps)) {
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
spin_unlock_irqrestore(&sess->ts_spinlock, flags);
return;
}
...
}
When list_empty() is true, the function returns early without decrementing
sess->esparser_queued_bufs. If this triggers enough times, the counter
artificially hits the max limit, preventing new buffers from being queued.
[Severity: High]
Does the break statement in esparser_queue_all_src() inadvertently cause an
infinite loop?
drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src() {
while (1) {
scoped_guard(mutex, &sess->lock) {
...
if (!vbuf)
break;
...
}
...
}
}
Because scoped_guard() is implemented using an internal for-loop, calling
break here only exits the scoped_guard's internal scope, not the outer
while (1) loop. This causes the worker thread to permanently hang in an
infinite busy-wait loop.
[Severity: High]
Can the failure to propagate an error in esparser_pad_start_code() cause
hardware out-of-bounds reads?
drivers/staging/media/meson/vdec/esparser.c:esparser_pad_start_code() {
...
if (!sess || READ_ONCE(sess->should_stop) || !sess->priv || !vaddr)
return 0;
...
}
If a client provides an unmapped buffer, !vaddr is true and this returns 0,
skipping the mandatory 512-byte start code padding.
Its caller esparser_queue() ignores this and passes the physical address to
esparser_write_data(), which unconditionally adds 512 to the payload size.
Since the padding wasn't written, the hardware DMA engine reads past the
valid buffer boundary.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=14
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 14/19] media: meson: vdec: Correct atomic counter placement in dst_buf_done
@ 2026-07-13 12:48 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:48 UTC (permalink / raw)
To: Anand Moon; +Cc: dri-devel, linux-amlogic, neil.armstrong
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [Critical] The patch introduces an unhandled double decrement of `sess->esparser_queued_bufs`, causing the atomic counter to underflow, corrupting stream state evaluation, and deadlocking the driver.
- [High] Using `break` inside `scoped_guard` fails to exit the worker loop, resulting in a 100% CPU infinite loop deadlock.
- [High] Returning 0 on `!vaddr` in `esparser_pad_start_code()` skips writing the mandatory start code pattern, leading to hardware out-of-bounds reads.
- [Medium] The commit message erroneously claims to relocate a decrement from the 'entry point' of the function, presenting a false premise for the change.
Pre-existing issues:
- [High] Missing decrement of `esparser_queued_bufs` on error paths permanently leaks logical buffer slots.
--
--- Patch [14]: [PATCH v7 14/19] media: meson: vdec: Correct atomic counter placement in dst_buf_done ---
commit 3bacd92d37472439ff6f7550b68eb503f9d760f0
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Correct atomic counter placement in dst_buf_done
This commit fixes an end-of-stream boundary condition by relocating
the atomic decrement helper to execute after state evaluation. It
aims to ensure accurate stream termination signaling.
> Previously, decrementing sess->esparser_queued_bufs at the entry point
> of the function corrupted the conditional checks for the final video
> frame.
[Severity: Medium]
Is this description accurate? Looking at the baseline code, dst_buf_done()
never contained an atomic decrement at its entry point or anywhere else
inside the function. The decrement only existed in the caller functions.
> diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.c b/drivers/staging/media/meson/vdec/vdec_helpers.c
> index f02c21d5a9c18..303236f0647c0 100644
> --- a/drivers/staging/media/meson/vdec/vdec_helpers.c
> +++ b/drivers/staging/media/meson/vdec/vdec_helpers.c
> @@ -314,6 +314,9 @@ static void dst_buf_done(struct amvdec_session *sess,
> dev_dbg(dev, "Buffer %u done, ts = %llu, flags = %08X\n",
> vbuf->vb2_buf.index, timestamp, flags);
> vbuf->field = field;
> +
> + atomic_dec(&sess->esparser_queued_bufs);
> +
[Severity: Critical]
Does this introduce a double decrement? Since the callers like
amvdec_dst_buf_done() and amvdec_dst_buf_done_offset() still contain their
original atomic_dec() calls, this new decrement causes the counter to
underflow.
When the counter becomes negative, the EOS condition
atomic_read(&sess->esparser_queued_bufs) <= 1 evaluates to true, which
can falsely trigger V4L2_EVENT_EOS on the wrong buffer and cause a
permanent -EAGAIN deadlock.
> v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
>
> /* Buffer done probably means the vififo got freed */
[Severity: High]
This is a pre-existing issue, but does the error path in amvdec_dst_buf_done()
permanently leak logical buffer slots?
drivers/staging/media/meson/vdec/vdec_helpers.c:amvdec_dst_buf_done() {
...
if (list_empty(&sess->timestamps)) {
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
spin_unlock_irqrestore(&sess->ts_spinlock, flags);
return;
}
...
}
When list_empty() is true, the function returns early without decrementing
sess->esparser_queued_bufs. If this triggers enough times, the counter
artificially hits the max limit, preventing new buffers from being queued.
[Severity: High]
Does the break statement in esparser_queue_all_src() inadvertently cause an
infinite loop?
drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src() {
while (1) {
scoped_guard(mutex, &sess->lock) {
...
if (!vbuf)
break;
...
}
...
}
}
Because scoped_guard() is implemented using an internal for-loop, calling
break here only exits the scoped_guard's internal scope, not the outer
while (1) loop. This causes the worker thread to permanently hang in an
infinite busy-wait loop.
[Severity: High]
Can the failure to propagate an error in esparser_pad_start_code() cause
hardware out-of-bounds reads?
drivers/staging/media/meson/vdec/esparser.c:esparser_pad_start_code() {
...
if (!sess || READ_ONCE(sess->should_stop) || !sess->priv || !vaddr)
return 0;
...
}
If a client provides an unmapped buffer, !vaddr is true and this returns 0,
skipping the mandatory 512-byte start code padding.
Its caller esparser_queue() ignores this and passes the physical address to
esparser_write_data(), which unconditionally adds 512 to the payload size.
Since the padding wasn't written, the hardware DMA engine reads past the
valid buffer boundary.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=14
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 12/19] media: meson: vdec: Coordinate m2m task execution inside async loop
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:54 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:54 UTC (permalink / raw)
To: Anand Moon; +Cc: dri-devel, neil.armstrong, linux-amlogic
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
New issues:
- [Critical] An AB-BA deadlock occurs between the V4L2 `VIDIOC_STREAMOFF` ioctl and the workqueue worker `esparser_queue_all_src` over `sess->lock`.
- [Critical] A recursive mutex self-deadlock occurs because `core->lock` is manually acquired inside functions already protected by the V4L2 core.
- [High] The `esparser_queue_all_src` workqueue thread enters a 100% CPU infinite busy-loop on empty queues, end-of-stream buffers, or hardware FIFO full conditions.
- [High] The V4L2 M2M framework permanently stalls due to a leaked job context when `esparser_queue_all_src` exits early or hits loop break conditions without calling `v4l2_m2m_job_finish()`.
- [High] Closing any secondary file descriptor unconditionally clears `core->cur_sess`, stripping hardware ownership from the active session and causing permanent DMA memory leaks.
Pre-existing issues:
- [Critical] An unconditional out-of-bounds heap write occurs when padding artificially small, user-controlled buffers in `esparser_pad_start_code()`.
- [Critical] An integer overflow during VP9 sub-frame length parsing allows attackers to bypass size boundaries and trigger an out-of-bounds `memmove` copy.
--
--- Patch [12]: [PATCH v7 12/19] media: meson: vdec: Coordinate m2m task execution inside async loop ---
commit 8f8ffb4492573d6d82ee209ecb382a9f5fbed9e4
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Coordinate m2m task execution inside async loop
This commit introduces proper V4L2 Memory-to-Memory (m2m) scheduler pipeline
tracking by handling job finalisation inside the asynchronous esparser
workqueue. It defers the scheduling call to v4l2_m2m_job_finish() to execute
within the worker routine.
> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> index b9f36fef4be12..939b239c2af47 100644
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
[ ... ]
> @@ -437,6 +438,9 @@ void esparser_queue_all_src(struct work_struct *work)
> else
> v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
>
> + /* Safely notify the V4L2 core sub-framework */
> + v4l2_m2m_job_finish(core->m2m_dev, sess->m2m_ctx);
> +
> /* Set tracking flag indicating transaction completion */
> processed_frame = true;
> }
[Severity: High]
Does this worker thread run into an infinite busy-loop on empty queues or
end-of-stream buffers?
In esparser_queue_all_src(), the code uses a scoped_guard for the lock:
while (1) {
...
scoped_guard(mutex, &sess->lock) {
if (!vbuf)
break;
}
}
Since scoped_guard expands into a hidden for-loop, the break statement only
exits the scoped_guard's loop, not the outer while (1) loop. This could trap
execution indefinitely, consuming 100% CPU when the queue is empty, hits an
EOS buffer, or the hardware FIFO is full.
[Severity: High]
Could the V4L2 M2M job context permanently leak if the session shuts down?
Looking at esparser_queue_all_src():
scoped_guard(mutex, &sess->lock) {
/* Safe atomic tracking check: exit loop if session is shutting down */
if (READ_ONCE(sess->should_stop))
return;
Returning early directly bypasses the newly added v4l2_m2m_job_finish() call.
This could permanently hang the V4L2 memory-to-memory framework because it
considers the device busy forever, preventing further decoding jobs.
[Severity: Critical]
Is there a risk of an AB-BA deadlock between the V4L2 VIDIOC_STREAMOFF ioctl
and the workqueue worker?
The V4L2 core framework acquires sess->lock during VIDIOC_STREAMOFF and then
calls vdec_stop_streaming(), which waits for the active worker thread:
drivers/staging/media/meson/vdec/vdec.c:vdec_stop_streaming() {
...
cancel_work_sync(&sess->esparser_queue_work);
}
Concurrently, the worker thread esparser_queue_all_src spins up and blocks
attempting to acquire the identical sess->lock:
drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src() {
...
scoped_guard(mutex, &sess->lock) {
}
This sequence would entirely deadlock the system when stopping the video stream.
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index 0eb39aa6014ee..b3e1d99e8889f 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -149,6 +149,17 @@ vdec_queue_recycle(struct amvdec_session *sess, struct vb2_buffer *vb)
> static void vdec_m2m_device_run(void *priv)
> {
> struct amvdec_session *sess = priv;
> + struct amvdec_core *core = sess->core;
> +
> + if (READ_ONCE(sess->should_stop)) {
> + v4l2_m2m_job_finish(core->m2m_dev, sess->m2m_ctx);
> + return;
> + }
> +
> + mutex_lock(&core->lock);
> + if (!core->cur_sess)
> + core->cur_sess = sess;
> + mutex_unlock(&core->lock);
>
> schedule_work(&sess->esparser_queue_work);
> }
[Severity: Critical]
Will acquiring core->lock manually here and in vdec_start_streaming result
in a recursive mutex self-deadlock?
Because vdev->queue is intentionally left NULL for M2M devices, the V4L2 core
ioctl wrapper unconditionally acquires vdev->lock (which is mapped to
&core->lock) before delegating execution to the ioctl implementations.
The underlying driver callback vdec_start_streaming then attempts to manually
acquire it again:
drivers/staging/media/meson/vdec/vdec.c:vdec_start_streaming() {
...
mutex_lock(&core->lock);
if (core->cur_sess && core->cur_sess != sess) {
}
This would immediately self-deadlock the kernel thread processing
VIDIOC_STREAMON or VIDIOC_STREAMOFF.
[Severity: High]
Does closing a secondary file descriptor unintentionally strip hardware
ownership from the active session?
In vdec_close(), the state modification is placed outside the active session
check block:
drivers/staging/media/meson/vdec/vdec.c:vdec_close() {
...
/* Safely clear hardware ownership since we were confirmed as the owner */
smp_store_release(&core->cur_sess, NULL);
}
If a user-space process opens the video node twice, streams on the first
session, and then closes the second session, this clears core->cur_sess
unconditionally. This destroys the ownership state of the active session,
bypassing its cleanup sequence and permanently leaking DMA buffers and canvases.
[Severity: Critical]
This is a pre-existing issue, but can esparser_pad_start_code() cause an
out-of-bounds heap write when handling artificially small buffers?
If userspace negotiates a small sizeimage and queues a tiny buffer, the padding
function computes pad_size based on ESPARSER_MIN_PACKET_SIZE and executes
a memset before verifying the vb2_plane_size:
drivers/staging/media/meson/vdec/esparser.c:esparser_pad_start_code() {
...
pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size;
memset(vaddr + payload_size, 0, pad_size);
}
if ((payload_size + pad_size + SEARCH_PATTERN_LEN) >
vb2_plane_size(vb, 0)) {
}
This could allow a local user to overwrite memory with zeroes by passing a
payload_size of 0, resulting in a 4096-byte memset.
[Severity: Critical]
This is a pre-existing issue, but might an integer overflow in
vp9_update_header() allow attackers to trigger an out-of-bounds memmove?
The parser reconstructs frame lengths by bit-shifting byte payloads:
drivers/staging/media/meson/vdec/esparser.c:vp9_update_header() {
...
frame_size[cur_frame] |=
(dp[mag_ptr] << (cur_mag * 8));
}
If dp[mag_ptr] is >= 128 and cur_mag is 3, the unsigned 8-bit value promotes
to an int and shifts into the sign bit, resulting in a large negative integer.
Later, this negative frame_size is passed to memmove():
drivers/staging/media/meson/vdec/esparser.c:vp9_update_header() {
...
memmove(fdata + VP9_HEADER_SIZE, old_framedata, framesize);
}
The negative framesize implicitly converts to a massive size_t, which could
corrupt arbitrary kernel memory.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=12
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 12/19] media: meson: vdec: Coordinate m2m task execution inside async loop
@ 2026-07-13 12:54 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:54 UTC (permalink / raw)
To: Anand Moon; +Cc: dri-devel, neil.armstrong, linux-amlogic
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
New issues:
- [Critical] An AB-BA deadlock occurs between the V4L2 `VIDIOC_STREAMOFF` ioctl and the workqueue worker `esparser_queue_all_src` over `sess->lock`.
- [Critical] A recursive mutex self-deadlock occurs because `core->lock` is manually acquired inside functions already protected by the V4L2 core.
- [High] The `esparser_queue_all_src` workqueue thread enters a 100% CPU infinite busy-loop on empty queues, end-of-stream buffers, or hardware FIFO full conditions.
- [High] The V4L2 M2M framework permanently stalls due to a leaked job context when `esparser_queue_all_src` exits early or hits loop break conditions without calling `v4l2_m2m_job_finish()`.
- [High] Closing any secondary file descriptor unconditionally clears `core->cur_sess`, stripping hardware ownership from the active session and causing permanent DMA memory leaks.
Pre-existing issues:
- [Critical] An unconditional out-of-bounds heap write occurs when padding artificially small, user-controlled buffers in `esparser_pad_start_code()`.
- [Critical] An integer overflow during VP9 sub-frame length parsing allows attackers to bypass size boundaries and trigger an out-of-bounds `memmove` copy.
--
--- Patch [12]: [PATCH v7 12/19] media: meson: vdec: Coordinate m2m task execution inside async loop ---
commit 8f8ffb4492573d6d82ee209ecb382a9f5fbed9e4
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Coordinate m2m task execution inside async loop
This commit introduces proper V4L2 Memory-to-Memory (m2m) scheduler pipeline
tracking by handling job finalisation inside the asynchronous esparser
workqueue. It defers the scheduling call to v4l2_m2m_job_finish() to execute
within the worker routine.
> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> index b9f36fef4be12..939b239c2af47 100644
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
[ ... ]
> @@ -437,6 +438,9 @@ void esparser_queue_all_src(struct work_struct *work)
> else
> v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
>
> + /* Safely notify the V4L2 core sub-framework */
> + v4l2_m2m_job_finish(core->m2m_dev, sess->m2m_ctx);
> +
> /* Set tracking flag indicating transaction completion */
> processed_frame = true;
> }
[Severity: High]
Does this worker thread run into an infinite busy-loop on empty queues or
end-of-stream buffers?
In esparser_queue_all_src(), the code uses a scoped_guard for the lock:
while (1) {
...
scoped_guard(mutex, &sess->lock) {
if (!vbuf)
break;
}
}
Since scoped_guard expands into a hidden for-loop, the break statement only
exits the scoped_guard's loop, not the outer while (1) loop. This could trap
execution indefinitely, consuming 100% CPU when the queue is empty, hits an
EOS buffer, or the hardware FIFO is full.
[Severity: High]
Could the V4L2 M2M job context permanently leak if the session shuts down?
Looking at esparser_queue_all_src():
scoped_guard(mutex, &sess->lock) {
/* Safe atomic tracking check: exit loop if session is shutting down */
if (READ_ONCE(sess->should_stop))
return;
Returning early directly bypasses the newly added v4l2_m2m_job_finish() call.
This could permanently hang the V4L2 memory-to-memory framework because it
considers the device busy forever, preventing further decoding jobs.
[Severity: Critical]
Is there a risk of an AB-BA deadlock between the V4L2 VIDIOC_STREAMOFF ioctl
and the workqueue worker?
The V4L2 core framework acquires sess->lock during VIDIOC_STREAMOFF and then
calls vdec_stop_streaming(), which waits for the active worker thread:
drivers/staging/media/meson/vdec/vdec.c:vdec_stop_streaming() {
...
cancel_work_sync(&sess->esparser_queue_work);
}
Concurrently, the worker thread esparser_queue_all_src spins up and blocks
attempting to acquire the identical sess->lock:
drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src() {
...
scoped_guard(mutex, &sess->lock) {
}
This sequence would entirely deadlock the system when stopping the video stream.
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index 0eb39aa6014ee..b3e1d99e8889f 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -149,6 +149,17 @@ vdec_queue_recycle(struct amvdec_session *sess, struct vb2_buffer *vb)
> static void vdec_m2m_device_run(void *priv)
> {
> struct amvdec_session *sess = priv;
> + struct amvdec_core *core = sess->core;
> +
> + if (READ_ONCE(sess->should_stop)) {
> + v4l2_m2m_job_finish(core->m2m_dev, sess->m2m_ctx);
> + return;
> + }
> +
> + mutex_lock(&core->lock);
> + if (!core->cur_sess)
> + core->cur_sess = sess;
> + mutex_unlock(&core->lock);
>
> schedule_work(&sess->esparser_queue_work);
> }
[Severity: Critical]
Will acquiring core->lock manually here and in vdec_start_streaming result
in a recursive mutex self-deadlock?
Because vdev->queue is intentionally left NULL for M2M devices, the V4L2 core
ioctl wrapper unconditionally acquires vdev->lock (which is mapped to
&core->lock) before delegating execution to the ioctl implementations.
The underlying driver callback vdec_start_streaming then attempts to manually
acquire it again:
drivers/staging/media/meson/vdec/vdec.c:vdec_start_streaming() {
...
mutex_lock(&core->lock);
if (core->cur_sess && core->cur_sess != sess) {
}
This would immediately self-deadlock the kernel thread processing
VIDIOC_STREAMON or VIDIOC_STREAMOFF.
[Severity: High]
Does closing a secondary file descriptor unintentionally strip hardware
ownership from the active session?
In vdec_close(), the state modification is placed outside the active session
check block:
drivers/staging/media/meson/vdec/vdec.c:vdec_close() {
...
/* Safely clear hardware ownership since we were confirmed as the owner */
smp_store_release(&core->cur_sess, NULL);
}
If a user-space process opens the video node twice, streams on the first
session, and then closes the second session, this clears core->cur_sess
unconditionally. This destroys the ownership state of the active session,
bypassing its cleanup sequence and permanently leaking DMA buffers and canvases.
[Severity: Critical]
This is a pre-existing issue, but can esparser_pad_start_code() cause an
out-of-bounds heap write when handling artificially small buffers?
If userspace negotiates a small sizeimage and queues a tiny buffer, the padding
function computes pad_size based on ESPARSER_MIN_PACKET_SIZE and executes
a memset before verifying the vb2_plane_size:
drivers/staging/media/meson/vdec/esparser.c:esparser_pad_start_code() {
...
pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size;
memset(vaddr + payload_size, 0, pad_size);
}
if ((payload_size + pad_size + SEARCH_PATTERN_LEN) >
vb2_plane_size(vb, 0)) {
}
This could allow a local user to overwrite memory with zeroes by passing a
payload_size of 0, resulting in a 4096-byte memset.
[Severity: Critical]
This is a pre-existing issue, but might an integer overflow in
vp9_update_header() allow attackers to trigger an out-of-bounds memmove?
The parser reconstructs frame lengths by bit-shifting byte payloads:
drivers/staging/media/meson/vdec/esparser.c:vp9_update_header() {
...
frame_size[cur_frame] |=
(dp[mag_ptr] << (cur_mag * 8));
}
If dp[mag_ptr] is >= 128 and cur_mag is 3, the unsigned 8-bit value promotes
to an int and shifts into the sign bit, resulting in a large negative integer.
Later, this negative frame_size is passed to memmove():
drivers/staging/media/meson/vdec/esparser.c:vp9_update_header() {
...
memmove(fdata + VP9_HEADER_SIZE, old_framedata, framesize);
}
The negative framesize implicitly converts to a massive size_t, which could
corrupt arbitrary kernel memory.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=12
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 13/19] media: meson: vdec: Fix race conditions in job abort sequence
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:55 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:55 UTC (permalink / raw)
To: Anand Moon; +Cc: linux-amlogic, dri-devel, neil.armstrong
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] Clearing the active session pointer while the hardware is still running causes unacknowledged interrupts and a CPU-locking interrupt storm.
- [High] Premature clearing of the active session pointer in job_abort bypasses hardware teardown and resource cleanup, causing severe memory and resource leaks.
- [High] AB-BA lock inversion deadlock between VIDIOC_STREAMOFF and the esparser_queue_work worker thread.
- [High] Releasing session ownership without stopping the hardware allows concurrent DMA overwrites and hardware state corruption.
- [High] Infinite loop inside esparser_queue_all_src() causing 100% CPU lockup when the queue is empty.
--
--- Patch [13]: [PATCH v7 13/19] media: meson: vdec: Fix race conditions in job abort sequence ---
commit bc32f101c548ab648eb44ae46f9cc2ea72d4c339
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix race conditions in job abort sequence
Implement proper cleanup inside vdec_m2m_job_abort to safely stop
the hardware job sequence during a streaming abort or teardown.
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index b3e1d99e8889f..ac86a9c4febff 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -167,6 +167,17 @@ static void vdec_m2m_device_run(void *priv)
> static void vdec_m2m_job_abort(void *priv)
> {
> struct amvdec_session *sess = priv;
> + struct amvdec_core *core = sess->core;
> +
> + WRITE_ONCE(sess->should_stop, 1);
> +
> + cancel_work_sync(&sess->esparser_queue_work);
[Severity: High]
Does this introduce an AB-BA deadlock with VIDIOC_STREAMOFF?
The V4L2 core acquires the queue lock (sess->lock) during VIDIOC_STREAMOFF
and calls vdec_m2m_job_abort(), which blocks here indefinitely waiting for
the worker.
Meanwhile, the worker thread unconditionally attempts to acquire sess->lock
in esparser_queue_all_src():
drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src() {
...
scoped_guard(mutex, &sess->lock) {
...
}
> +
> + mutex_lock(&core->lock);
> + if (core->cur_sess == sess)
> + /* Safely clear hardware ownership since we were confirmed as the owner */
> + smp_store_release(&core->cur_sess, NULL);
[Severity: High]
Does clearing cur_sess here prematurely bypass hardware teardown?
During a normal VIDIOC_STREAMOFF, vdec_m2m_job_abort() is called before
the stop_streaming hook. If cur_sess is cleared here, the later teardown
in vdec_stop_streaming() will skip powering off the hardware and freeing
the DMA buffer (vififo_size) because core->cur_sess == sess evaluates to
false:
drivers/staging/media/meson/vdec/vdec.c:vdec_stop_streaming() {
...
if (core->cur_sess == sess) {
vdec_poweroff(sess);
vdec_free_canvas(sess);
...
}
[Severity: Critical]
Can this cause an interrupt storm and CPU lockup?
Since the physical hardware isn't stopped, it may complete its active task
and assert a level-triggered interrupt.
The ISR checks cur_sess, reads NULL, and immediately returns IRQ_HANDLED
without acknowledging or clearing the hardware register:
drivers/staging/media/meson/vdec/vdec.c:vdec_isr() {
...
if (!sess)
return IRQ_HANDLED;
...
}
Will this cause the unacknowledged interrupt to immediately fire again,
locking up the CPU?
[Severity: High]
Could this allow concurrent DMA overwrites and hardware state corruption?
By clearing core->cur_sess here, the V4L2 framework considers the hardware
idle and can schedule a new session.
Since the hardware is actually still running, a newly scheduled session
might blindly overwrite the active hardware's registers in esparser_queue(),
corrupting the ongoing DMA operations:
drivers/staging/media/meson/vdec/esparser.c:esparser_queue() {
...
amvdec_write_parser(core, PARSER_VIDEO_START_PTR, payload_paddr);
...
}
> + mutex_unlock(&core->lock);
>
> v4l2_m2m_job_finish(sess->core->m2m_dev, sess->m2m_ctx);
> }
[Severity: High]
Does the esparser_queue_all_src() worker have an infinite loop?
When looking into the interaction with esparser_queue_work, the outer
loop in esparser_queue_all_src() appears to never terminate when the
queue is empty:
drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src() {
while (1) {
struct vb2_v4l2_buffer *vbuf = NULL;
bool processed_frame = false;
...
scoped_guard(mutex, &sess->lock) {
...
vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
if (!vbuf)
break;
...
}
...
}
}
Since scoped_guard is implemented as a hidden for-loop, the break
statement only exits the scoped_guard's internal loop, not the outer
while(1) loop.
Because processed_frame remains false, the code calls cond_resched() and
spins indefinitely. Could this cause a 100% CPU lockup?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=13
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 13/19] media: meson: vdec: Fix race conditions in job abort sequence
@ 2026-07-13 12:55 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:55 UTC (permalink / raw)
To: Anand Moon; +Cc: linux-amlogic, dri-devel, neil.armstrong
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] Clearing the active session pointer while the hardware is still running causes unacknowledged interrupts and a CPU-locking interrupt storm.
- [High] Premature clearing of the active session pointer in job_abort bypasses hardware teardown and resource cleanup, causing severe memory and resource leaks.
- [High] AB-BA lock inversion deadlock between VIDIOC_STREAMOFF and the esparser_queue_work worker thread.
- [High] Releasing session ownership without stopping the hardware allows concurrent DMA overwrites and hardware state corruption.
- [High] Infinite loop inside esparser_queue_all_src() causing 100% CPU lockup when the queue is empty.
--
--- Patch [13]: [PATCH v7 13/19] media: meson: vdec: Fix race conditions in job abort sequence ---
commit bc32f101c548ab648eb44ae46f9cc2ea72d4c339
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix race conditions in job abort sequence
Implement proper cleanup inside vdec_m2m_job_abort to safely stop
the hardware job sequence during a streaming abort or teardown.
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index b3e1d99e8889f..ac86a9c4febff 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -167,6 +167,17 @@ static void vdec_m2m_device_run(void *priv)
> static void vdec_m2m_job_abort(void *priv)
> {
> struct amvdec_session *sess = priv;
> + struct amvdec_core *core = sess->core;
> +
> + WRITE_ONCE(sess->should_stop, 1);
> +
> + cancel_work_sync(&sess->esparser_queue_work);
[Severity: High]
Does this introduce an AB-BA deadlock with VIDIOC_STREAMOFF?
The V4L2 core acquires the queue lock (sess->lock) during VIDIOC_STREAMOFF
and calls vdec_m2m_job_abort(), which blocks here indefinitely waiting for
the worker.
Meanwhile, the worker thread unconditionally attempts to acquire sess->lock
in esparser_queue_all_src():
drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src() {
...
scoped_guard(mutex, &sess->lock) {
...
}
> +
> + mutex_lock(&core->lock);
> + if (core->cur_sess == sess)
> + /* Safely clear hardware ownership since we were confirmed as the owner */
> + smp_store_release(&core->cur_sess, NULL);
[Severity: High]
Does clearing cur_sess here prematurely bypass hardware teardown?
During a normal VIDIOC_STREAMOFF, vdec_m2m_job_abort() is called before
the stop_streaming hook. If cur_sess is cleared here, the later teardown
in vdec_stop_streaming() will skip powering off the hardware and freeing
the DMA buffer (vififo_size) because core->cur_sess == sess evaluates to
false:
drivers/staging/media/meson/vdec/vdec.c:vdec_stop_streaming() {
...
if (core->cur_sess == sess) {
vdec_poweroff(sess);
vdec_free_canvas(sess);
...
}
[Severity: Critical]
Can this cause an interrupt storm and CPU lockup?
Since the physical hardware isn't stopped, it may complete its active task
and assert a level-triggered interrupt.
The ISR checks cur_sess, reads NULL, and immediately returns IRQ_HANDLED
without acknowledging or clearing the hardware register:
drivers/staging/media/meson/vdec/vdec.c:vdec_isr() {
...
if (!sess)
return IRQ_HANDLED;
...
}
Will this cause the unacknowledged interrupt to immediately fire again,
locking up the CPU?
[Severity: High]
Could this allow concurrent DMA overwrites and hardware state corruption?
By clearing core->cur_sess here, the V4L2 framework considers the hardware
idle and can schedule a new session.
Since the hardware is actually still running, a newly scheduled session
might blindly overwrite the active hardware's registers in esparser_queue(),
corrupting the ongoing DMA operations:
drivers/staging/media/meson/vdec/esparser.c:esparser_queue() {
...
amvdec_write_parser(core, PARSER_VIDEO_START_PTR, payload_paddr);
...
}
> + mutex_unlock(&core->lock);
>
> v4l2_m2m_job_finish(sess->core->m2m_dev, sess->m2m_ctx);
> }
[Severity: High]
Does the esparser_queue_all_src() worker have an infinite loop?
When looking into the interaction with esparser_queue_work, the outer
loop in esparser_queue_all_src() appears to never terminate when the
queue is empty:
drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src() {
while (1) {
struct vb2_v4l2_buffer *vbuf = NULL;
bool processed_frame = false;
...
scoped_guard(mutex, &sess->lock) {
...
vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
if (!vbuf)
break;
...
}
...
}
}
Since scoped_guard is implemented as a hidden for-loop, the break
statement only exits the scoped_guard's internal loop, not the outer
while(1) loop.
Because processed_frame remains false, the code calls cond_resched() and
spins indefinitely. Could this cause a 100% CPU lockup?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=13
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 16/19] media: meson: vdec: Configure DMA mask and segment size in probe
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:55 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:55 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, dri-devel, linux-amlogic
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [High] Hardware interrupts are not masked prior to calling `synchronize_irq()`, leading to a race condition that causes a Use-After-Free of `sess->priv`.
- [High] Misuse of `scoped_guard` inside a `while (1)` loop combined with inverted logic causes the workqueue to busy-wait infinitely when no data is available.
- [Medium] Missing release barriers when publishing `core->cur_sess` allows the lockless ISR to observe an uninitialized or stale session state.
Pre-existing issues:
- [Critical] vp9_update_header trusts user-provided frame sizes to calculate offsets, leading to integer overflows and massive out-of-bounds memory accesses via memmove.
- [High] `meson_canvas_get` retrieves a pointer to the provider's private data but immediately drops its reference to the provider device, leading to a Use-After-Free if the provider driver is unbound.
- [Medium] Potential NULL pointer dereference if the driver is probed without a device tree node.
--
--- Patch [16]: [PATCH v7 16/19] media: meson: vdec: Configure DMA mask and segment size in probe ---
commit 8dd369d994b7e2a6a1fb0db5dcba10a32f46beaa
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Configure DMA mask and segment size in probe
This commit addresses missing explicit DMA constraints in the vdec probe
routine. It enforces a 32-bit coherent DMA allocation window and maximizes
the contiguous allocation segment boundary constraint to UINT_MAX.
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -1186,6 +1186,16 @@ static int vdec_probe(struct platform_device *pdev)
> if (IS_ERR(core->canvas))
> return PTR_ERR(core->canvas);
>
> + /* Enforce strict 32-bit DMA limit to match hardware capabilities */
> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to set 32-bit DMA mask\n");
> +
> + ret = vb2_dma_contig_set_max_seg_size(dev, UINT_MAX);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "Failed to set DMA max segment size\n");
> +
> of_id = of_match_node(vdec_dt_match, dev->of_node);
> core->platform = of_id->data;
[Severity: Medium]
This isn't a bug introduced by this patch, but can this lead to a NULL pointer
dereference if the driver is probed without a device tree node? If a user
with privileges binds the driver manually via sysfs without an associated
device tree node, dev->of_node is NULL. The of_match_node() call will
return NULL, causing of_id->data to be dereferenced blindly.
[Severity: High]
Can this regression cause a use-after-free of sess->priv in the interrupt
handler?
drivers/staging/media/meson/vdec/vdec.c:vdec_stop_streaming() {
...
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
/* Ensure esparser ISR finishes executing */
synchronize_irq(core->esparser_irq);
cancel_work_sync(&sess->esparser_queue_work);
mutex_lock(&core->lock);
if (core->cur_sess == sess) {
vdec_poweroff(sess);
...
}
It appears hardware interrupts are not masked prior to calling
synchronize_irq(), which could allow an interrupt to fire immediately
after synchronize_irq() returns. Because vdec_poweroff() is responsible
for masking the hardware interrupts, could the main thread then free
sess->priv while the concurrently executing ISR dereferences it?
[Severity: High]
Does this loop correctly process the buffer queue or does it spin infinitely?
drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src() {
...
while (1) {
struct vb2_v4l2_buffer *vbuf = NULL;
bool processed_frame = false;
scoped_guard(mutex, &sess->lock) {
/* Safe atomic tracking check: exit loop if session is shutting down */
if (READ_ONCE(sess->should_stop))
return;
/* Queue completely empty: exit work loop cleanly */
vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
if (!vbuf)
break;
...
}
When v4l2_m2m_next_src_buf() returns NULL, the break statement seems to only
exit the inner for loop created by the scoped_guard macro. This bypasses the
subsequent loop exit conditions and repeats the while (1) loop endlessly,
potentially causing a CPU lockup on the workqueue thread. Additionally, if a
buffer is present, could this break out of the loop completely instead of
processing the queue?
[Severity: Medium]
Can the lockless ISR observe an uninitialized or stale session state due to
missing release barriers?
drivers/staging/media/meson/vdec/vdec.c:vdec_start_streaming() {
...
/* If already half-initialized, do not re-initialize */
if (sess->status == STATUS_INIT) {
mutex_unlock(&core->lock);
return 0;
}
sess->status = STATUS_INIT;
core->cur_sess = sess;
mutex_unlock(&core->lock);
...
}
Because core->cur_sess is assigned using a plain store, could the compiler or
CPU reorder this store before earlier initialization steps like
sess->status = STATUS_INIT? If the lockless ISR uses smp_load_acquire(), it
might observe the new session pointer but the old memory state.
[Severity: High]
This is a pre-existing issue, but does this code cause a use-after-free if the
provider driver is unbound?
drivers/soc/amlogic/meson-canvas.c:meson_canvas_get() {
...
canvas_pdev = of_find_device_by_node(canvas_node);
of_node_put(canvas_node);
if (!canvas_pdev)
return ERR_PTR(-EPROBE_DEFER);
...
canvas = dev_get_drvdata(&canvas_pdev->dev);
put_device(&canvas_pdev->dev);
if (!canvas)
return ERR_PTR(-EINVAL);
return canvas;
}
The put_device() call immediately drops the device reference before returning
the pointer. Since no device link is maintained, could an admin or
unprivileged user with sysfs access unbind the amlogic-canvas driver,
freeing the memory while the vdec driver still holds and uses the
dangling pointer?
[Severity: Critical]
This is a pre-existing issue, but does this size calculation prevent integer
overflows and out-of-bounds memory accesses?
drivers/staging/media/meson/vdec/esparser.c:vp9_update_header() {
...
for (cur_frame = 0; cur_frame < num_frames; cur_frame++) {
frame_size[cur_frame] = 0;
for (cur_mag = 0; cur_mag < mag; cur_mag++) {
if (mag_ptr >= dsize)
return -EINVAL;
frame_size[cur_frame] |=
(dp[mag_ptr] << (cur_mag * 8));
mag_ptr++;
}
if (cur_frame == 0)
tot_frame_size[cur_frame] =
frame_size[cur_frame];
else
tot_frame_size[cur_frame] =
tot_frame_size[cur_frame - 1] +
frame_size[cur_frame];
total_datasize += frame_size[cur_frame];
}
...
}
It appears user-provided frame sizes are added blindly without overflow
protection. If a user crafts a V4L2 VP9 buffer payload with two frames
specifying sizes that sum to 0 (e.g., 0x40000000 and 0xC0000000), could
this bypass the new_frame_size bounds check? The driver might then use
one of the huge frame sizes in a memmove operation, leading to
out-of-bounds reads and writes on the kernel heap.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=16
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 16/19] media: meson: vdec: Configure DMA mask and segment size in probe
@ 2026-07-13 12:55 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:55 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, dri-devel, linux-amlogic
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [High] Hardware interrupts are not masked prior to calling `synchronize_irq()`, leading to a race condition that causes a Use-After-Free of `sess->priv`.
- [High] Misuse of `scoped_guard` inside a `while (1)` loop combined with inverted logic causes the workqueue to busy-wait infinitely when no data is available.
- [Medium] Missing release barriers when publishing `core->cur_sess` allows the lockless ISR to observe an uninitialized or stale session state.
Pre-existing issues:
- [Critical] vp9_update_header trusts user-provided frame sizes to calculate offsets, leading to integer overflows and massive out-of-bounds memory accesses via memmove.
- [High] `meson_canvas_get` retrieves a pointer to the provider's private data but immediately drops its reference to the provider device, leading to a Use-After-Free if the provider driver is unbound.
- [Medium] Potential NULL pointer dereference if the driver is probed without a device tree node.
--
--- Patch [16]: [PATCH v7 16/19] media: meson: vdec: Configure DMA mask and segment size in probe ---
commit 8dd369d994b7e2a6a1fb0db5dcba10a32f46beaa
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Configure DMA mask and segment size in probe
This commit addresses missing explicit DMA constraints in the vdec probe
routine. It enforces a 32-bit coherent DMA allocation window and maximizes
the contiguous allocation segment boundary constraint to UINT_MAX.
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -1186,6 +1186,16 @@ static int vdec_probe(struct platform_device *pdev)
> if (IS_ERR(core->canvas))
> return PTR_ERR(core->canvas);
>
> + /* Enforce strict 32-bit DMA limit to match hardware capabilities */
> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to set 32-bit DMA mask\n");
> +
> + ret = vb2_dma_contig_set_max_seg_size(dev, UINT_MAX);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "Failed to set DMA max segment size\n");
> +
> of_id = of_match_node(vdec_dt_match, dev->of_node);
> core->platform = of_id->data;
[Severity: Medium]
This isn't a bug introduced by this patch, but can this lead to a NULL pointer
dereference if the driver is probed without a device tree node? If a user
with privileges binds the driver manually via sysfs without an associated
device tree node, dev->of_node is NULL. The of_match_node() call will
return NULL, causing of_id->data to be dereferenced blindly.
[Severity: High]
Can this regression cause a use-after-free of sess->priv in the interrupt
handler?
drivers/staging/media/meson/vdec/vdec.c:vdec_stop_streaming() {
...
/* Synchronize and flush pending hardware interrupt service routines */
synchronize_irq(core->vdec_irq);
/* Ensure esparser ISR finishes executing */
synchronize_irq(core->esparser_irq);
cancel_work_sync(&sess->esparser_queue_work);
mutex_lock(&core->lock);
if (core->cur_sess == sess) {
vdec_poweroff(sess);
...
}
It appears hardware interrupts are not masked prior to calling
synchronize_irq(), which could allow an interrupt to fire immediately
after synchronize_irq() returns. Because vdec_poweroff() is responsible
for masking the hardware interrupts, could the main thread then free
sess->priv while the concurrently executing ISR dereferences it?
[Severity: High]
Does this loop correctly process the buffer queue or does it spin infinitely?
drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src() {
...
while (1) {
struct vb2_v4l2_buffer *vbuf = NULL;
bool processed_frame = false;
scoped_guard(mutex, &sess->lock) {
/* Safe atomic tracking check: exit loop if session is shutting down */
if (READ_ONCE(sess->should_stop))
return;
/* Queue completely empty: exit work loop cleanly */
vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
if (!vbuf)
break;
...
}
When v4l2_m2m_next_src_buf() returns NULL, the break statement seems to only
exit the inner for loop created by the scoped_guard macro. This bypasses the
subsequent loop exit conditions and repeats the while (1) loop endlessly,
potentially causing a CPU lockup on the workqueue thread. Additionally, if a
buffer is present, could this break out of the loop completely instead of
processing the queue?
[Severity: Medium]
Can the lockless ISR observe an uninitialized or stale session state due to
missing release barriers?
drivers/staging/media/meson/vdec/vdec.c:vdec_start_streaming() {
...
/* If already half-initialized, do not re-initialize */
if (sess->status == STATUS_INIT) {
mutex_unlock(&core->lock);
return 0;
}
sess->status = STATUS_INIT;
core->cur_sess = sess;
mutex_unlock(&core->lock);
...
}
Because core->cur_sess is assigned using a plain store, could the compiler or
CPU reorder this store before earlier initialization steps like
sess->status = STATUS_INIT? If the lockless ISR uses smp_load_acquire(), it
might observe the new session pointer but the old memory state.
[Severity: High]
This is a pre-existing issue, but does this code cause a use-after-free if the
provider driver is unbound?
drivers/soc/amlogic/meson-canvas.c:meson_canvas_get() {
...
canvas_pdev = of_find_device_by_node(canvas_node);
of_node_put(canvas_node);
if (!canvas_pdev)
return ERR_PTR(-EPROBE_DEFER);
...
canvas = dev_get_drvdata(&canvas_pdev->dev);
put_device(&canvas_pdev->dev);
if (!canvas)
return ERR_PTR(-EINVAL);
return canvas;
}
The put_device() call immediately drops the device reference before returning
the pointer. Since no device link is maintained, could an admin or
unprivileged user with sysfs access unbind the amlogic-canvas driver,
freeing the memory while the vdec driver still holds and uses the
dangling pointer?
[Severity: Critical]
This is a pre-existing issue, but does this size calculation prevent integer
overflows and out-of-bounds memory accesses?
drivers/staging/media/meson/vdec/esparser.c:vp9_update_header() {
...
for (cur_frame = 0; cur_frame < num_frames; cur_frame++) {
frame_size[cur_frame] = 0;
for (cur_mag = 0; cur_mag < mag; cur_mag++) {
if (mag_ptr >= dsize)
return -EINVAL;
frame_size[cur_frame] |=
(dp[mag_ptr] << (cur_mag * 8));
mag_ptr++;
}
if (cur_frame == 0)
tot_frame_size[cur_frame] =
frame_size[cur_frame];
else
tot_frame_size[cur_frame] =
tot_frame_size[cur_frame - 1] +
frame_size[cur_frame];
total_datasize += frame_size[cur_frame];
}
...
}
It appears user-provided frame sizes are added blindly without overflow
protection. If a user crafts a V4L2 VP9 buffer payload with two frames
specifying sizes that sum to 0 (e.g., 0x40000000 and 0xC0000000), could
this bypass the new_frame_size bounds check? The driver might then use
one of the huge frame sizes in a memmove operation, leading to
out-of-bounds reads and writes on the kernel heap.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=16
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 15/19] media: meson: vdec: Fix concurrent firmware loading race and hardware timeout
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:56 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:56 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, linux-amlogic, dri-devel
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [Low] Suboptimal use of atomic polling in a sleepable context.
Pre-existing issues:
- [High] Missing memory barrier between firmware memcpy and DMA start.
- [High] Incomplete fix: identical bugs (static variables and raw decrement loop) remain in HEVC firmware loader.
- [High] Potential DMA Use-After-Free if firmware DMA transfer times out.
--
--- Patch [15]: [PATCH v7 15/19] media: meson: vdec: Fix concurrent firmware loading race and hardware timeout ---
commit 7acf244d598b1d1a15fbe219b6ceaead31b2210b
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix concurrent firmware loading race and hardware timeout
This commit fixes a concurrent firmware loading race condition by replacing
static variables with local ones in vdec_1_load_firmware(). It also addresses
hardware DMA hang timeouts by replacing a fast decrement loop with
readl_poll_timeout_atomic().
> diff --git a/drivers/staging/media/meson/vdec/vdec_1.c b/drivers/staging/media/meson/vdec/vdec_1.c
> --- a/drivers/staging/media/meson/vdec/vdec_1.c
> +++ b/drivers/staging/media/meson/vdec/vdec_1.c
[ ... ]
> @@ -63,11 +63,11 @@ vdec_1_load_firmware(struct amvdec_session *sess, const char *fwname)
> amvdec_write_dos(core, IMEM_DMA_COUNT, MC_SIZE / 4);
> amvdec_write_dos(core, IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
[Severity: High]
This is a pre-existing issue, but is there a missing memory barrier between
the firmware memcpy and the DMA start?
Earlier in vdec_1_load_firmware(), the firmware is copied to a DMA-coherent
buffer:
memcpy(mc_addr, fw->data, MC_SIZE);
Since amvdec_write_dos() maps to writel_relaxed(), there is no memory-to-I/O
ordering guarantee. Could the CPU issue the MMIO write to start the DMA
before the firmware data is flushed to main memory, causing the hardware to
read stale data?
> - while (--i && amvdec_read_dos(core, IMEM_DMA_CTRL) & 0x8000);
> -
> - if (i == 0) {
> + ret = readl_poll_timeout_atomic(core->dos_base + IMEM_DMA_CTRL, val,
> + !(val & 0x8000), 10, 10000);
[Severity: Low]
Does this need to use the atomic polling variant?
Since vdec_1_load_firmware() runs in a sleepable context (it successfully
calls request_firmware() and allocates memory with GFP_KERNEL just prior),
could this use the non-atomic readl_poll_timeout() macro instead?
Using the atomic variant forces the CPU to busy-wait in a udelay() loop for
up to 10ms, which might be inefficient.
> + if (ret) {
> dev_err(dev, "Firmware load fail (DMA hang?)\n");
> - ret = -EINVAL;
> + ret = -ETIMEDOUT;
> goto free_mc;
[Severity: High]
This is a pre-existing issue, but does this create a DMA use-after-free if
the firmware DMA transfer times out?
When a timeout occurs, the code jumps to free_mc:
free_mc:
dma_free_coherent(core->dev, MC_SIZE, mc_addr, mc_addr_map);
If the hardware is merely slow and still executing the DMA transfer, will it
continue to read from these freed memory pages? Should the DMA controller be
explicitly halted (for example, by clearing the IMEM_DMA_CTRL register)
before releasing the buffer?
> }
[Severity: High]
This is a pre-existing issue, but does the same problem still exist in the
HEVC firmware loader?
Looking at vdec_hevc_load_firmware() in vdec_hevc.c, it uses the exact same
static variables and fast decrement loop that this patch fixes in vdec_1.c:
static int vdec_hevc_load_firmware(...)
{
...
static void *mc_addr;
static dma_addr_t mc_addr_map;
...
while (i && (readl(core->dos_base + HEVC_IMEM_DMA_CTRL) & 0x8000))
i--;
...
}
Will HEVC decoding also fail consistently on modern CPUs due to false DMA
timeouts? And could multiple HEVC decoding sessions corrupt state by sharing
these static variables?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=15
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 15/19] media: meson: vdec: Fix concurrent firmware loading race and hardware timeout
@ 2026-07-13 12:56 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:56 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, linux-amlogic, dri-devel
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [Low] Suboptimal use of atomic polling in a sleepable context.
Pre-existing issues:
- [High] Missing memory barrier between firmware memcpy and DMA start.
- [High] Incomplete fix: identical bugs (static variables and raw decrement loop) remain in HEVC firmware loader.
- [High] Potential DMA Use-After-Free if firmware DMA transfer times out.
--
--- Patch [15]: [PATCH v7 15/19] media: meson: vdec: Fix concurrent firmware loading race and hardware timeout ---
commit 7acf244d598b1d1a15fbe219b6ceaead31b2210b
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Fix concurrent firmware loading race and hardware timeout
This commit fixes a concurrent firmware loading race condition by replacing
static variables with local ones in vdec_1_load_firmware(). It also addresses
hardware DMA hang timeouts by replacing a fast decrement loop with
readl_poll_timeout_atomic().
> diff --git a/drivers/staging/media/meson/vdec/vdec_1.c b/drivers/staging/media/meson/vdec/vdec_1.c
> --- a/drivers/staging/media/meson/vdec/vdec_1.c
> +++ b/drivers/staging/media/meson/vdec/vdec_1.c
[ ... ]
> @@ -63,11 +63,11 @@ vdec_1_load_firmware(struct amvdec_session *sess, const char *fwname)
> amvdec_write_dos(core, IMEM_DMA_COUNT, MC_SIZE / 4);
> amvdec_write_dos(core, IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
[Severity: High]
This is a pre-existing issue, but is there a missing memory barrier between
the firmware memcpy and the DMA start?
Earlier in vdec_1_load_firmware(), the firmware is copied to a DMA-coherent
buffer:
memcpy(mc_addr, fw->data, MC_SIZE);
Since amvdec_write_dos() maps to writel_relaxed(), there is no memory-to-I/O
ordering guarantee. Could the CPU issue the MMIO write to start the DMA
before the firmware data is flushed to main memory, causing the hardware to
read stale data?
> - while (--i && amvdec_read_dos(core, IMEM_DMA_CTRL) & 0x8000);
> -
> - if (i == 0) {
> + ret = readl_poll_timeout_atomic(core->dos_base + IMEM_DMA_CTRL, val,
> + !(val & 0x8000), 10, 10000);
[Severity: Low]
Does this need to use the atomic polling variant?
Since vdec_1_load_firmware() runs in a sleepable context (it successfully
calls request_firmware() and allocates memory with GFP_KERNEL just prior),
could this use the non-atomic readl_poll_timeout() macro instead?
Using the atomic variant forces the CPU to busy-wait in a udelay() loop for
up to 10ms, which might be inefficient.
> + if (ret) {
> dev_err(dev, "Firmware load fail (DMA hang?)\n");
> - ret = -EINVAL;
> + ret = -ETIMEDOUT;
> goto free_mc;
[Severity: High]
This is a pre-existing issue, but does this create a DMA use-after-free if
the firmware DMA transfer times out?
When a timeout occurs, the code jumps to free_mc:
free_mc:
dma_free_coherent(core->dev, MC_SIZE, mc_addr, mc_addr_map);
If the hardware is merely slow and still executing the DMA transfer, will it
continue to read from these freed memory pages? Should the DMA controller be
explicitly halted (for example, by clearing the IMEM_DMA_CTRL register)
before releasing the buffer?
> }
[Severity: High]
This is a pre-existing issue, but does the same problem still exist in the
HEVC firmware loader?
Looking at vdec_hevc_load_firmware() in vdec_hevc.c, it uses the exact same
static variables and fast decrement loop that this patch fixes in vdec_1.c:
static int vdec_hevc_load_firmware(...)
{
...
static void *mc_addr;
static dma_addr_t mc_addr_map;
...
while (i && (readl(core->dos_base + HEVC_IMEM_DMA_CTRL) & 0x8000))
i--;
...
}
Will HEVC decoding also fail consistently on modern CPUs due to false DMA
timeouts? And could multiple HEVC decoding sessions corrupt state by sharing
these static variables?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=15
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:57 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:57 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, dri-devel, linux-amlogic
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Uninitialized pointer dereference in the error handling path of `meson_drv_bind_master`.
Pre-existing issues:
- [High] NULL pointer dereference when `devm_kzalloc` fails to allocate the `priv` structure.
--
--- Patch [19]: [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries ---
commit 5509f37f742bf971664f2f0e723c6187dedff8d4
Author: Anand Moon <linux.amoon@gmail.com>
gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries
This commit addresses DMA layout configuration constraints by setting a 32-bit
coherent DMA allocation window and maximizing the scatter-gather allocation
segment boundary check constraint.
> diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
> index 49ff9f1f16d32..899e70bca4ce2 100644
> --- a/drivers/gpu/drm/meson/meson_drv.c
> +++ b/drivers/gpu/drm/meson/meson_drv.c
> @@ -202,6 +202,12 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
> if (IS_ERR(drm))
> return PTR_ERR(drm);
>
> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + goto free_drm;
[Severity: High]
If dma_set_mask_and_coherent() fails in meson_drv_bind_master(), the code jumps
to the free_drm error path before the priv pointer is allocated and initialized.
Because the free_drm label unconditionally passes priv to encoder removal
functions:
drivers/gpu/drm/meson/meson_drv.c:meson_drv_bind_master() {
...
free_drm:
drm_dev_put(drm);
meson_encoder_dsi_remove(priv);
...
}
And meson_encoder_dsi_remove() dereferences priv immediately:
drivers/gpu/drm/meson/meson_encoder_dsi.c:meson_encoder_dsi_remove() {
...
if (priv->encoders[MESON_ENC_DSI]) {
...
}
Will this result in dereferencing an uninitialized stack pointer?
> +
> + dma_set_max_seg_size(dev, UINT_MAX);
> +
> priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> if (!priv) {
> ret = -ENOMEM;
> goto free_drm;
[Severity: High]
This is a pre-existing issue, but if devm_kzalloc() in meson_drv_bind_master()
fails to allocate priv, it will be set to NULL and the code jumps to free_drm.
Similar to the above issue, this will pass a NULL priv pointer to
meson_encoder_dsi_remove(), which does not check for NULL before dereferencing
it:
drivers/gpu/drm/meson/meson_encoder_dsi.c:meson_encoder_dsi_remove() {
...
if (priv->encoders[MESON_ENC_DSI]) {
...
}
Will this cause a NULL pointer dereference during an out-of-memory condition?
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=19
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries
@ 2026-07-13 12:57 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:57 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, dri-devel, linux-amlogic
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Uninitialized pointer dereference in the error handling path of `meson_drv_bind_master`.
Pre-existing issues:
- [High] NULL pointer dereference when `devm_kzalloc` fails to allocate the `priv` structure.
--
--- Patch [19]: [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries ---
commit 5509f37f742bf971664f2f0e723c6187dedff8d4
Author: Anand Moon <linux.amoon@gmail.com>
gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries
This commit addresses DMA layout configuration constraints by setting a 32-bit
coherent DMA allocation window and maximizing the scatter-gather allocation
segment boundary check constraint.
> diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
> index 49ff9f1f16d32..899e70bca4ce2 100644
> --- a/drivers/gpu/drm/meson/meson_drv.c
> +++ b/drivers/gpu/drm/meson/meson_drv.c
> @@ -202,6 +202,12 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
> if (IS_ERR(drm))
> return PTR_ERR(drm);
>
> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + goto free_drm;
[Severity: High]
If dma_set_mask_and_coherent() fails in meson_drv_bind_master(), the code jumps
to the free_drm error path before the priv pointer is allocated and initialized.
Because the free_drm label unconditionally passes priv to encoder removal
functions:
drivers/gpu/drm/meson/meson_drv.c:meson_drv_bind_master() {
...
free_drm:
drm_dev_put(drm);
meson_encoder_dsi_remove(priv);
...
}
And meson_encoder_dsi_remove() dereferences priv immediately:
drivers/gpu/drm/meson/meson_encoder_dsi.c:meson_encoder_dsi_remove() {
...
if (priv->encoders[MESON_ENC_DSI]) {
...
}
Will this result in dereferencing an uninitialized stack pointer?
> +
> + dma_set_max_seg_size(dev, UINT_MAX);
> +
> priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> if (!priv) {
> ret = -ENOMEM;
> goto free_drm;
[Severity: High]
This is a pre-existing issue, but if devm_kzalloc() in meson_drv_bind_master()
fails to allocate priv, it will be set to NULL and the code jumps to free_drm.
Similar to the above issue, this will pass a NULL priv pointer to
meson_encoder_dsi_remove(), which does not check for NULL before dereferencing
it:
drivers/gpu/drm/meson/meson_encoder_dsi.c:meson_encoder_dsi_remove() {
...
if (priv->encoders[MESON_ENC_DSI]) {
...
}
Will this cause a NULL pointer dereference during an out-of-memory condition?
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=19
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 17/19] media: meson: canvas: Fix Use-After-Free by linking canvas provider device
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 12:58 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:58 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, linux-amlogic, dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] A Time-of-Check to Time-of-Use (TOCTOU) race condition exists between retrieving the supplier's driver data and establishing the device link, which allows the supplier to unbind and free the data before the link can protect it.
Pre-existing issues:
- [High] The device link fails to prevent the Use-After-Free in the `vdec` driver because V4L2 file descriptors outlive the consumer's driver unbind sequence.
--
--- Patch [17]: [PATCH v7 17/19] media: meson: canvas: Fix Use-After-Free by linking canvas provider device ---
commit bf6a22d368b30413921052e4cb659c5a279efaea
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: canvas: Fix Use-After-Free by linking canvas provider device
This commit attempts to fix a Use-After-Free by establishing a device link
between the vdec consumer and the canvas provider. The link aims to
automatically unbind the consumer driver when the provider unbinds.
Closes: https://lore.kernel.org/all/20260530111022.9C6D71F00893@smtp.kernel.org
> diff --git a/drivers/soc/amlogic/meson-canvas.c b/drivers/soc/amlogic/meson-canvas.c
> index 79681afea8c61..5102a491bf778 100644
> --- a/drivers/soc/amlogic/meson-canvas.c
> +++ b/drivers/soc/amlogic/meson-canvas.c
[ ... ]
> @@ -70,9 +71,18 @@ struct meson_canvas *meson_canvas_get(struct device *dev)
> * current state, this driver probe cannot return -EPROBE_DEFER
> */
> canvas = dev_get_drvdata(&canvas_pdev->dev);
[Severity: High]
Is there a race condition between retrieving the driver data and creating
the device link?
If the canvas provider unbinds immediately after dev_get_drvdata() but
before device_link_add(), devres_release_all() will free the canvas data.
Because the driver core allows links to unbound suppliers, device_link_add()
would still succeed and create a dormant link, and then meson_canvas_get()
would return a freed pointer to the caller.
> - put_device(&canvas_pdev->dev);
> - if (!canvas)
> + if (!canvas) {
> + put_device(&canvas_pdev->dev);
> return ERR_PTR(-EINVAL);
> + }
> +
> + /* Establish device link to prevent Use-After-Free */
> + link = device_link_add(dev, &canvas_pdev->dev,
> + DL_FLAG_AUTOREMOVE_CONSUMER);
[Severity: High]
This is a pre-existing issue, but does this device link fully resolve the
use-after-free for the vdec driver?
Because vdec is a V4L2 driver, userspace file descriptors can outlive the
consumer driver's unbind sequence. If a user holds the video device open:
1. The canvas provider unbinds.
2. The device link forces the vdec driver to unbind.
3. devres_release_all() frees the devm-managed canvas and vdec core contexts.
4. Userspace later closes the file descriptor.
When the file descriptor is closed, vdec_close() calls vdec_free_canvas(),
which attempts to dereference the already freed sess->core->canvas pointer.
> + put_device(&canvas_pdev->dev);
> + if (!link)
> + return ERR_PTR(dev_err_probe(dev, -EINVAL,
> + "Failed to create device link canvas\n"));
>
> return canvas;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=17
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 17/19] media: meson: canvas: Fix Use-After-Free by linking canvas provider device
@ 2026-07-13 12:58 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 12:58 UTC (permalink / raw)
To: Anand Moon; +Cc: neil.armstrong, linux-amlogic, dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] A Time-of-Check to Time-of-Use (TOCTOU) race condition exists between retrieving the supplier's driver data and establishing the device link, which allows the supplier to unbind and free the data before the link can protect it.
Pre-existing issues:
- [High] The device link fails to prevent the Use-After-Free in the `vdec` driver because V4L2 file descriptors outlive the consumer's driver unbind sequence.
--
--- Patch [17]: [PATCH v7 17/19] media: meson: canvas: Fix Use-After-Free by linking canvas provider device ---
commit bf6a22d368b30413921052e4cb659c5a279efaea
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: canvas: Fix Use-After-Free by linking canvas provider device
This commit attempts to fix a Use-After-Free by establishing a device link
between the vdec consumer and the canvas provider. The link aims to
automatically unbind the consumer driver when the provider unbinds.
Closes: https://lore.kernel.org/all/20260530111022.9C6D71F00893@smtp.kernel.org
> diff --git a/drivers/soc/amlogic/meson-canvas.c b/drivers/soc/amlogic/meson-canvas.c
> index 79681afea8c61..5102a491bf778 100644
> --- a/drivers/soc/amlogic/meson-canvas.c
> +++ b/drivers/soc/amlogic/meson-canvas.c
[ ... ]
> @@ -70,9 +71,18 @@ struct meson_canvas *meson_canvas_get(struct device *dev)
> * current state, this driver probe cannot return -EPROBE_DEFER
> */
> canvas = dev_get_drvdata(&canvas_pdev->dev);
[Severity: High]
Is there a race condition between retrieving the driver data and creating
the device link?
If the canvas provider unbinds immediately after dev_get_drvdata() but
before device_link_add(), devres_release_all() will free the canvas data.
Because the driver core allows links to unbound suppliers, device_link_add()
would still succeed and create a dormant link, and then meson_canvas_get()
would return a freed pointer to the caller.
> - put_device(&canvas_pdev->dev);
> - if (!canvas)
> + if (!canvas) {
> + put_device(&canvas_pdev->dev);
> return ERR_PTR(-EINVAL);
> + }
> +
> + /* Establish device link to prevent Use-After-Free */
> + link = device_link_add(dev, &canvas_pdev->dev,
> + DL_FLAG_AUTOREMOVE_CONSUMER);
[Severity: High]
This is a pre-existing issue, but does this device link fully resolve the
use-after-free for the vdec driver?
Because vdec is a V4L2 driver, userspace file descriptors can outlive the
consumer driver's unbind sequence. If a user holds the video device open:
1. The canvas provider unbinds.
2. The device link forces the vdec driver to unbind.
3. devres_release_all() frees the devm-managed canvas and vdec core contexts.
4. Userspace later closes the file descriptor.
When the file descriptor is closed, vdec_close() calls vdec_free_canvas(),
which attempts to dereference the already freed sess->core->canvas pointer.
> + put_device(&canvas_pdev->dev);
> + if (!link)
> + return ERR_PTR(dev_err_probe(dev, -EINVAL,
> + "Failed to create device link canvas\n"));
>
> return canvas;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=17
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 18/19] media: meson: vdec: Increase VIFIFO buffer size to 32 MiB
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 13:06 ` sashiko-bot
-1 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 13:06 UTC (permalink / raw)
To: Anand Moon; +Cc: linux-amlogic, dri-devel, neil.armstrong
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
New issues:
- [High] The `esparser_queue_all_src` workqueue handler contains a fatal infinite loop due to incorrect usage of the `scoped_guard` macro in combination with `break` statements, monopolizing 100% of a CPU core.
- [High] Double decrement of the `esparser_queued_bufs` atomic counter per video frame leads to counter underflow and buffer overruns.
- [High] The 32 MiB VIFIFO buffer increase causes the bitstream size to overflow the 32-bit hardware PARSER_CONTROL register calculation, crashing the esparser DMA engine.
Pre-existing issues:
- [Critical] In `vp9_update_header`, an integer overflow when decoding maliciously crafted frame magnitudes allows `frame_size` to become negative, bypassing size validations and triggering a massive out-of-bounds `memmove` that panics the kernel.
- [High] Physical address truncation for the hardware ring buffer end pointer causes immediate DMA corruption when memory is allocated near the top of the 32-bit address space.
- [High] Use-After-Free (UAF) vulnerabilities in `vdec_close` during device unbind due to `devm_kzalloc` and `v4l2_m2m_release` lifecycle mismatches.
- [Medium] Timestamp tracking and matching breaks silently when the 32-bit hardware offset wraps around at the 4 GB bitstream boundary, due to an improper naive unsigned sequence comparison.
- [Medium] Race condition in `esparser_write_data` overwrites the completion flag, causing false input parsing timeouts.
--
--- Patch [18]: [PATCH v7 18/19] media: meson: vdec: Increase VIFIFO buffer size to 32 MiB ---
commit 2addc7a4961dc5fcc69dfbb2e96d57eb89853de9
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Increase VIFIFO buffer size to 32 MiB
During 4K video playback, the hardware Elementary Stream Parser (esparser)
can rapidly push massive burst data payloads into the Video Input FIFO
faster than the VPU decoding block can consume it. This causes a buffer
overflow when stream usage breaks past the allocated hardware boundary.
This patch doubles the parsed bitstream swap exchange memory pool to 32 MiB.
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index d33cbebc4453b..824e2f156adeb 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -29,8 +29,8 @@ struct dummy_buf {
> struct list_head list;
> };
>
> -/* 16 MiB for parsed bitstream swap exchange */
> -#define SIZE_VIFIFO SZ_16M
> +/* 32 MiB for parsed bitstream swap exchange */
> +#define SIZE_VIFIFO SZ_32M
[Severity: High]
Does increasing the buffer to 32 MiB cause the bitstream size to overflow
the 32-bit hardware PARSER_CONTROL register calculation?
In drivers/staging/media/meson/vdec/esparser.c:esparser_write_data():
amvdec_write_parser(core, PARSER_CONTROL,
ES_WRITE |
ES_PARSER_START |
ES_SEARCH |
(size << ES_PACK_SIZE_BIT));
With ES_PACK_SIZE_BIT being 8, for a size >= 16 MiB this left shift will
overflow a 32-bit unsigned integer. This can result in the DMA engine
receiving a truncated payload size and crashing.
[Severity: High]
This is a pre-existing issue, but does increasing the buffer size make it
highly probable that the hardware ring buffer end pointer truncates, causing
immediate DMA corruption?
In drivers/staging/media/meson/vdec/vdec_hevc.c:vdec_hevc_stbuf_init():
amvdec_write_dos(core, HEVC_STREAM_END_ADDR,
sess->vififo_paddr + sess->vififo_size);
If the VIFIFO buffer is allocated near the end of the 32-bit address space,
the calculation sess->vififo_paddr + sess->vififo_size can exceed 32 bits,
resulting in a value like 0x100000000. This is silently truncated to 0 when
passed to amvdec_write_dos() which accepts a 32-bit u32.
[Severity: Medium]
This is a pre-existing issue, but how does the driver handle timestamps when
the 32-bit hardware offset wraps around at the 4 GB bitstream boundary?
In drivers/staging/media/meson/vdec/vdec_helpers.c:amvdec_dst_buf_done_offset():
list_for_each_entry_safe(tmp, n, &sess->timestamps, list) {
if (tmp->offset > offset) {
...
break;
}
The stream offset accumulates into a 32-bit integer in esparser_get_offset()
that naturally wraps at 4 GB. The list_for_each_entry_safe loop uses a
non-wrap-safe raw unsigned comparison. When the offset wraps around to 0,
a large tmp->offset immediately evaluates as greater and incorrectly breaks
the loop, stranding or dropping timestamps.
[Severity: High]
Are there Use-After-Free vulnerabilities in vdec_close() during device unbind
due to devm_kzalloc() and v4l2_m2m_release() lifecycle mismatches? This isn't
a bug introduced by this patch, but...
In drivers/staging/media/meson/vdec/vdec.c:vdec_remove():
video_unregister_device(core->vdev_dec);
v4l2_m2m_release(core->m2m_dev);
The unbind path frees the core structure while V4L2 file descriptors may
still be actively held by userspace.
Later, if an application closes the file descriptor, vdec_close() is invoked:
synchronize_irq(core->vdec_irq);
This could dereference the freed core pointer and the freed m2m_dev.
[Severity: High]
Does the esparser_queue_all_src() workqueue handler contain an infinite loop
that could monopolize a CPU core?
In drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src():
while (1) {
struct vb2_v4l2_buffer *vbuf = NULL;
bool processed_frame = false;
scoped_guard(mutex, &sess->lock) {
...
vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
if (!vbuf)
break;
...
}
if (processed_frame)
break;
...
}
The break statements are intended to exit the function when no buffers are
available. However, they only escape the scoped_guard's internal for loop.
This leaves the outer while (1) loop intact since processed_frame is false,
which could result in a lockup of a kernel worker thread.
[Severity: High]
Does this code double decrement the esparser_queued_bufs atomic counter?
In drivers/staging/media/meson/vdec/vdec_helpers.c, dst_buf_done() calls
atomic_dec(&sess->esparser_queued_bufs). However, its callers also execute
their own atomic_dec():
amvdec_dst_buf_done():
dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags);
atomic_dec(&sess->esparser_queued_bufs);
amvdec_dst_buf_done_offset():
dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags);
if (match)
atomic_dec(&sess->esparser_queued_bufs);
This could lead to counter underflow, bypassing buffer limit checks and
causing hardware queue overruns.
[Severity: Medium]
This is a pre-existing issue, but does esparser_write_data() contain a race
condition that overwrites the completion flag?
In drivers/staging/media/meson/vdec/esparser.c:esparser_write_data():
amvdec_write_parser(core, PARSER_FETCH_CMD,
(7 << FETCH_ENDIAN_BIT) |
(size + SEARCH_PATTERN_LEN));
search_done = 0;
return wait_event_interruptible_timeout(wq, search_done, (HZ / 5));
The CPU initiates hardware processing by writing to PARSER_FETCH_CMD, which
can instantly trigger esparser_isr(). If the hardware interrupt fires
immediately, the ISR sets search_done = 1. The CPU sequentially executes
search_done = 0 afterwards, which could erase the completion signal before
waiting on it.
[Severity: Critical]
This is a pre-existing issue, but does vp9_update_header() have an integer
overflow when decoding frame magnitudes that can bypass size validations?
In drivers/staging/media/meson/vdec/esparser.c:vp9_update_header():
frame_size[cur_frame] |=
(dp[mag_ptr] << (cur_mag * 8));
The magnitude byte is promoted to a signed 32-bit integer. When shifted by
24 bits, it can become a large negative number if the maliciously crafted
payload has dp[mag_ptr] >= 0x80.
Later, this negative frame size bypasses total_datasize validations and
triggers an out-of-bounds copy in memmove:
memmove(fdata + VP9_HEADER_SIZE, old_framedata, framesize);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=18
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 18/19] media: meson: vdec: Increase VIFIFO buffer size to 32 MiB
@ 2026-07-13 13:06 ` sashiko-bot
0 siblings, 0 replies; 102+ messages in thread
From: sashiko-bot @ 2026-07-13 13:06 UTC (permalink / raw)
To: Anand Moon; +Cc: linux-amlogic, dri-devel, neil.armstrong
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
New issues:
- [High] The `esparser_queue_all_src` workqueue handler contains a fatal infinite loop due to incorrect usage of the `scoped_guard` macro in combination with `break` statements, monopolizing 100% of a CPU core.
- [High] Double decrement of the `esparser_queued_bufs` atomic counter per video frame leads to counter underflow and buffer overruns.
- [High] The 32 MiB VIFIFO buffer increase causes the bitstream size to overflow the 32-bit hardware PARSER_CONTROL register calculation, crashing the esparser DMA engine.
Pre-existing issues:
- [Critical] In `vp9_update_header`, an integer overflow when decoding maliciously crafted frame magnitudes allows `frame_size` to become negative, bypassing size validations and triggering a massive out-of-bounds `memmove` that panics the kernel.
- [High] Physical address truncation for the hardware ring buffer end pointer causes immediate DMA corruption when memory is allocated near the top of the 32-bit address space.
- [High] Use-After-Free (UAF) vulnerabilities in `vdec_close` during device unbind due to `devm_kzalloc` and `v4l2_m2m_release` lifecycle mismatches.
- [Medium] Timestamp tracking and matching breaks silently when the 32-bit hardware offset wraps around at the 4 GB bitstream boundary, due to an improper naive unsigned sequence comparison.
- [Medium] Race condition in `esparser_write_data` overwrites the completion flag, causing false input parsing timeouts.
--
--- Patch [18]: [PATCH v7 18/19] media: meson: vdec: Increase VIFIFO buffer size to 32 MiB ---
commit 2addc7a4961dc5fcc69dfbb2e96d57eb89853de9
Author: Anand Moon <linux.amoon@gmail.com>
media: meson: vdec: Increase VIFIFO buffer size to 32 MiB
During 4K video playback, the hardware Elementary Stream Parser (esparser)
can rapidly push massive burst data payloads into the Video Input FIFO
faster than the VPU decoding block can consume it. This causes a buffer
overflow when stream usage breaks past the allocated hardware boundary.
This patch doubles the parsed bitstream swap exchange memory pool to 32 MiB.
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index d33cbebc4453b..824e2f156adeb 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
> @@ -29,8 +29,8 @@ struct dummy_buf {
> struct list_head list;
> };
>
> -/* 16 MiB for parsed bitstream swap exchange */
> -#define SIZE_VIFIFO SZ_16M
> +/* 32 MiB for parsed bitstream swap exchange */
> +#define SIZE_VIFIFO SZ_32M
[Severity: High]
Does increasing the buffer to 32 MiB cause the bitstream size to overflow
the 32-bit hardware PARSER_CONTROL register calculation?
In drivers/staging/media/meson/vdec/esparser.c:esparser_write_data():
amvdec_write_parser(core, PARSER_CONTROL,
ES_WRITE |
ES_PARSER_START |
ES_SEARCH |
(size << ES_PACK_SIZE_BIT));
With ES_PACK_SIZE_BIT being 8, for a size >= 16 MiB this left shift will
overflow a 32-bit unsigned integer. This can result in the DMA engine
receiving a truncated payload size and crashing.
[Severity: High]
This is a pre-existing issue, but does increasing the buffer size make it
highly probable that the hardware ring buffer end pointer truncates, causing
immediate DMA corruption?
In drivers/staging/media/meson/vdec/vdec_hevc.c:vdec_hevc_stbuf_init():
amvdec_write_dos(core, HEVC_STREAM_END_ADDR,
sess->vififo_paddr + sess->vififo_size);
If the VIFIFO buffer is allocated near the end of the 32-bit address space,
the calculation sess->vififo_paddr + sess->vififo_size can exceed 32 bits,
resulting in a value like 0x100000000. This is silently truncated to 0 when
passed to amvdec_write_dos() which accepts a 32-bit u32.
[Severity: Medium]
This is a pre-existing issue, but how does the driver handle timestamps when
the 32-bit hardware offset wraps around at the 4 GB bitstream boundary?
In drivers/staging/media/meson/vdec/vdec_helpers.c:amvdec_dst_buf_done_offset():
list_for_each_entry_safe(tmp, n, &sess->timestamps, list) {
if (tmp->offset > offset) {
...
break;
}
The stream offset accumulates into a 32-bit integer in esparser_get_offset()
that naturally wraps at 4 GB. The list_for_each_entry_safe loop uses a
non-wrap-safe raw unsigned comparison. When the offset wraps around to 0,
a large tmp->offset immediately evaluates as greater and incorrectly breaks
the loop, stranding or dropping timestamps.
[Severity: High]
Are there Use-After-Free vulnerabilities in vdec_close() during device unbind
due to devm_kzalloc() and v4l2_m2m_release() lifecycle mismatches? This isn't
a bug introduced by this patch, but...
In drivers/staging/media/meson/vdec/vdec.c:vdec_remove():
video_unregister_device(core->vdev_dec);
v4l2_m2m_release(core->m2m_dev);
The unbind path frees the core structure while V4L2 file descriptors may
still be actively held by userspace.
Later, if an application closes the file descriptor, vdec_close() is invoked:
synchronize_irq(core->vdec_irq);
This could dereference the freed core pointer and the freed m2m_dev.
[Severity: High]
Does the esparser_queue_all_src() workqueue handler contain an infinite loop
that could monopolize a CPU core?
In drivers/staging/media/meson/vdec/esparser.c:esparser_queue_all_src():
while (1) {
struct vb2_v4l2_buffer *vbuf = NULL;
bool processed_frame = false;
scoped_guard(mutex, &sess->lock) {
...
vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
if (!vbuf)
break;
...
}
if (processed_frame)
break;
...
}
The break statements are intended to exit the function when no buffers are
available. However, they only escape the scoped_guard's internal for loop.
This leaves the outer while (1) loop intact since processed_frame is false,
which could result in a lockup of a kernel worker thread.
[Severity: High]
Does this code double decrement the esparser_queued_bufs atomic counter?
In drivers/staging/media/meson/vdec/vdec_helpers.c, dst_buf_done() calls
atomic_dec(&sess->esparser_queued_bufs). However, its callers also execute
their own atomic_dec():
amvdec_dst_buf_done():
dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags);
atomic_dec(&sess->esparser_queued_bufs);
amvdec_dst_buf_done_offset():
dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags);
if (match)
atomic_dec(&sess->esparser_queued_bufs);
This could lead to counter underflow, bypassing buffer limit checks and
causing hardware queue overruns.
[Severity: Medium]
This is a pre-existing issue, but does esparser_write_data() contain a race
condition that overwrites the completion flag?
In drivers/staging/media/meson/vdec/esparser.c:esparser_write_data():
amvdec_write_parser(core, PARSER_FETCH_CMD,
(7 << FETCH_ENDIAN_BIT) |
(size + SEARCH_PATTERN_LEN));
search_done = 0;
return wait_event_interruptible_timeout(wq, search_done, (HZ / 5));
The CPU initiates hardware processing by writing to PARSER_FETCH_CMD, which
can instantly trigger esparser_isr(). If the hardware interrupt fires
immediately, the ISR sets search_done = 1. The CPU sequentially executes
search_done = 0 afterwards, which could erase the completion signal before
waiting on it.
[Severity: Critical]
This is a pre-existing issue, but does vp9_update_header() have an integer
overflow when decoding frame magnitudes that can bypass size validations?
In drivers/staging/media/meson/vdec/esparser.c:vp9_update_header():
frame_size[cur_frame] |=
(dp[mag_ptr] << (cur_mag * 8));
The magnitude byte is promoted to a signed 32-bit integer. When shifted by
24 bits, it can become a large negative number if the maliciously crafted
payload has dp[mag_ptr] >= 0x80.
Later, this negative frame size bypasses total_datasize validations and
triggers an out-of-bounds copy in memmove:
memmove(fdata + VP9_HEADER_SIZE, old_framedata, framesize);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=18
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries
2026-07-13 12:07 ` Anand Moon
@ 2026-07-13 14:04 ` Nicolas Dufresne
-1 siblings, 0 replies; 102+ messages in thread
From: Nicolas Dufresne @ 2026-07-13 14:04 UTC (permalink / raw)
To: Anand Moon, Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk
[-- Attachment #1: Type: text/plain, Size: 1889 bytes --]
Le lundi 13 juillet 2026 à 17:37 +0530, Anand Moon a écrit :
> When importing DMABUFs exported by the Amlogic video decoder driver
> (meson_vdec) for hardware-accelerated rendering paths, the DMA core
> subsystem throws constraint validation warnings. This occurs because the
> display controller master device lacks explicit DMA layout configuration,
> causing it to fall back to a default 64KB maximum segment size limit.
>
> Address these architectural constraints during the master bind sequence:
>
> 1. Initialize and validate a 32-bit coherent DMA allocation window by
> invoking dma_set_mask_and_coherent() with a DMA_BIT_MASK(32) argument.
> 2. Maximize the contiguous scatter-gather allocation segment boundary
> check constraint to UINT_MAX using the dma_set_max_seg_size() helper.
>
> This guarantees that large video bitstream frame buffers can be imported
> and scanned out across sub-driver domains without triggering allocation
> warnings or page boundary splits.
>
> Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
> Signed-off-by: Anand Moon <linux.amoon@gmail.com>
Acked-by: Nicolas Dufresne <nicolas@ndufresne.ca>
> ---
> drivers/gpu/drm/meson/meson_drv.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
> index 49ff9f1f16d32..899e70bca4ce2 100644
> --- a/drivers/gpu/drm/meson/meson_drv.c
> +++ b/drivers/gpu/drm/meson/meson_drv.c
> @@ -202,6 +202,12 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
> if (IS_ERR(drm))
> return PTR_ERR(drm);
>
> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + goto free_drm;
> +
> + dma_set_max_seg_size(dev, UINT_MAX);
> +
> priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> if (!priv) {
> ret = -ENOMEM;
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries
@ 2026-07-13 14:04 ` Nicolas Dufresne
0 siblings, 0 replies; 102+ messages in thread
From: Nicolas Dufresne @ 2026-07-13 14:04 UTC (permalink / raw)
To: Anand Moon, Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk
[-- Attachment #1.1: Type: text/plain, Size: 1889 bytes --]
Le lundi 13 juillet 2026 à 17:37 +0530, Anand Moon a écrit :
> When importing DMABUFs exported by the Amlogic video decoder driver
> (meson_vdec) for hardware-accelerated rendering paths, the DMA core
> subsystem throws constraint validation warnings. This occurs because the
> display controller master device lacks explicit DMA layout configuration,
> causing it to fall back to a default 64KB maximum segment size limit.
>
> Address these architectural constraints during the master bind sequence:
>
> 1. Initialize and validate a 32-bit coherent DMA allocation window by
> invoking dma_set_mask_and_coherent() with a DMA_BIT_MASK(32) argument.
> 2. Maximize the contiguous scatter-gather allocation segment boundary
> check constraint to UINT_MAX using the dma_set_max_seg_size() helper.
>
> This guarantees that large video bitstream frame buffers can be imported
> and scanned out across sub-driver domains without triggering allocation
> warnings or page boundary splits.
>
> Cc: Nicolas Dufresne <nicolas@ndufresne.ca>
> Signed-off-by: Anand Moon <linux.amoon@gmail.com>
Acked-by: Nicolas Dufresne <nicolas@ndufresne.ca>
> ---
> drivers/gpu/drm/meson/meson_drv.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
> index 49ff9f1f16d32..899e70bca4ce2 100644
> --- a/drivers/gpu/drm/meson/meson_drv.c
> +++ b/drivers/gpu/drm/meson/meson_drv.c
> @@ -202,6 +202,12 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
> if (IS_ERR(drm))
> return PTR_ERR(drm);
>
> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + goto free_drm;
> +
> + dma_set_max_seg_size(dev, UINT_MAX);
> +
> priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> if (!priv) {
> ret = -ENOMEM;
[-- Attachment #1.2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
[-- Attachment #2: Type: text/plain, Size: 167 bytes --]
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs
2026-07-13 12:06 ` Anand Moon
@ 2026-07-14 7:23 ` Anand Moon
-1 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-14 7:23 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk
Hi All,
On Mon, 13 Jul 2026 at 17:38, Anand Moon <linux.amoon@gmail.com> wrote:
>
> This patch series addresses a collection of critical race conditions,
> memory leaks, use-after-free bugs, and resource management issues
> within the Amlogic Meson video decoder (meson-vdec) driver and
> associated components.
>
> The primary focus is tightening session lifecycles, safely synchronising
> the V4L2 m2m framework callbacks, hardening work queue operations
> (esparser_queue_work), and correcting buffer validations to prevent
> stability issues like KASAN out-of-bounds errors or stream-on timeouts.
>
> Summary of Changes:
> * Race Conditions & Synchronisation: Fixes multi-threading and
> execution races across stream-on, stream-off, job abort, and
> teardown pathways.
> * Memory Lifecycle: Eliminates memory leaks and use-after-free
> vulnerabilities by safely pinning, canceling, and sequencing
> device removals.
> * VP9 & Buffer Hardening: Addresses payload handling bugs in the ES
> parser, properly tracks ongoing buffers via atomic counters, and
> prevents invalid memory writes.
> * Platform/DMA Adjustments: Adjusts DMA segment configurations,
> canvas handling, and buffer sizes to match modern hardware demands.
>
> Note on an outstanding issue:
> An execution deadlock occurs in the driver workqueue path during high
> stress or long playback sessions. This stall is triggered when the
> Amlogic hardware internal video FIFO buffer becomes completely full.
> When esparser_queue_all_src runs, the capacity validation check is
> tripped due to a design limitation in how buffer exhaustion is handled.
> This blocks progress and leaves the workqueue worker waiting indefinitely
> on a core mutex lock.
>
> The resulting hung task call trace shows the worker thread blocking
> on the internal lock:
>
> Workqueue: events esparser_queue_all_src [meson_vdec]
> ...
> __mutex_lock
> mutex_lock_nested
> esparser_queue_all_src
>
> I have tried to address this issue, but the underlying deadlock persists
> under extreme conditions.
>
> Any feedback on this new appock is welcome.
>
> Testing was done using v4l2-compliance and active decoding pipelines
> on Meson platforms (like Hardkernel ODROID-N2+ and ODROID-C4) SBCs.
>
> The v4l2-compliance tests passed successfully via:
> $ v4l2-compliance -s -v -d /dev/video0
>
> Pipelines were validated using the following GStreamer script.
>
> [1] https://gist.github.com/moonlinux/08295ba1f17ce7155550773f2d2cb6e5
>
> Previous changes:
> v6: https://lore.kernel.org/all/20260530094326.11892-2-linux.amoon@gmail.com/
>
I will review Sashiko's feedback. I will look into these suggestions and
work on incorporating them to improve the code quality
> Thanks
> -Annad
>
Thanks
-Anand
^ permalink raw reply [flat|nested] 102+ messages in thread
* Re: [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs
@ 2026-07-14 7:23 ` Anand Moon
0 siblings, 0 replies; 102+ messages in thread
From: Anand Moon @ 2026-07-14 7:23 UTC (permalink / raw)
To: Neil Armstrong, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Kevin Hilman,
Jerome Brunet, Martin Blumenstingl, Mauro Carvalho Chehab,
Greg Kroah-Hartman, open list:DRM DRIVERS FOR AMLOGIC SOCS,
open list:DRM DRIVERS FOR AMLOGIC SOCS,
moderated list:ARM/Amlogic Meson SoC support, open list,
open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS,
open list:STAGING SUBSYSTEM
Cc: Doruk Tan Ozturk
Hi All,
On Mon, 13 Jul 2026 at 17:38, Anand Moon <linux.amoon@gmail.com> wrote:
>
> This patch series addresses a collection of critical race conditions,
> memory leaks, use-after-free bugs, and resource management issues
> within the Amlogic Meson video decoder (meson-vdec) driver and
> associated components.
>
> The primary focus is tightening session lifecycles, safely synchronising
> the V4L2 m2m framework callbacks, hardening work queue operations
> (esparser_queue_work), and correcting buffer validations to prevent
> stability issues like KASAN out-of-bounds errors or stream-on timeouts.
>
> Summary of Changes:
> * Race Conditions & Synchronisation: Fixes multi-threading and
> execution races across stream-on, stream-off, job abort, and
> teardown pathways.
> * Memory Lifecycle: Eliminates memory leaks and use-after-free
> vulnerabilities by safely pinning, canceling, and sequencing
> device removals.
> * VP9 & Buffer Hardening: Addresses payload handling bugs in the ES
> parser, properly tracks ongoing buffers via atomic counters, and
> prevents invalid memory writes.
> * Platform/DMA Adjustments: Adjusts DMA segment configurations,
> canvas handling, and buffer sizes to match modern hardware demands.
>
> Note on an outstanding issue:
> An execution deadlock occurs in the driver workqueue path during high
> stress or long playback sessions. This stall is triggered when the
> Amlogic hardware internal video FIFO buffer becomes completely full.
> When esparser_queue_all_src runs, the capacity validation check is
> tripped due to a design limitation in how buffer exhaustion is handled.
> This blocks progress and leaves the workqueue worker waiting indefinitely
> on a core mutex lock.
>
> The resulting hung task call trace shows the worker thread blocking
> on the internal lock:
>
> Workqueue: events esparser_queue_all_src [meson_vdec]
> ...
> __mutex_lock
> mutex_lock_nested
> esparser_queue_all_src
>
> I have tried to address this issue, but the underlying deadlock persists
> under extreme conditions.
>
> Any feedback on this new appock is welcome.
>
> Testing was done using v4l2-compliance and active decoding pipelines
> on Meson platforms (like Hardkernel ODROID-N2+ and ODROID-C4) SBCs.
>
> The v4l2-compliance tests passed successfully via:
> $ v4l2-compliance -s -v -d /dev/video0
>
> Pipelines were validated using the following GStreamer script.
>
> [1] https://gist.github.com/moonlinux/08295ba1f17ce7155550773f2d2cb6e5
>
> Previous changes:
> v6: https://lore.kernel.org/all/20260530094326.11892-2-linux.amoon@gmail.com/
>
I will review Sashiko's feedback. I will look into these suggestions and
work on incorporating them to improve the code quality
> Thanks
> -Annad
>
Thanks
-Anand
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 102+ messages in thread
end of thread, other threads:[~2026-07-14 7:24 UTC | newest]
Thread overview: 102+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13 12:06 [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
2026-07-13 12:06 ` Anand Moon
2026-07-13 12:06 ` Anand Moon
2026-07-13 12:06 ` [PATCH v7 01/19] media: meson: vdec: Fix m2m device lifetime and cleanup path Anand Moon
2026-07-13 12:06 ` Anand Moon
2026-07-13 12:06 ` Anand Moon
2026-07-13 12:35 ` sashiko-bot
2026-07-13 12:35 ` sashiko-bot
2026-07-13 12:06 ` [PATCH v7 02/19] media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown Anand Moon
2026-07-13 12:06 ` Anand Moon
2026-07-13 12:06 ` Anand Moon
2026-07-13 12:38 ` sashiko-bot
2026-07-13 12:38 ` sashiko-bot
2026-07-13 12:06 ` [PATCH v7 03/19] media: meson: vdec: Fix lifecycle leaks and race conditions in recycle_thread Anand Moon
2026-07-13 12:06 ` Anand Moon
2026-07-13 12:06 ` Anand Moon
2026-07-13 12:32 ` sashiko-bot
2026-07-13 12:32 ` sashiko-bot
2026-07-13 12:06 ` [PATCH v7 04/19] media: meson: vdec: Fix use-after-free race between teardown and ISR routines Anand Moon
2026-07-13 12:06 ` Anand Moon
2026-07-13 12:06 ` Anand Moon
2026-07-13 12:23 ` sashiko-bot
2026-07-13 12:23 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 05/19] media: meson: vdec: Fix race condition and synchronize esparser IRQ Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:28 ` sashiko-bot
2026-07-13 12:28 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 06/19] media: meson: vdec: Fix race condition by canceling work sync Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:33 ` sashiko-bot
2026-07-13 12:33 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 07/19] media: meson: vdec: Refactor esparser work queue and fix teardown race Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:27 ` sashiko-bot
2026-07-13 12:27 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 08/19] media: meson: vdec: Fix concurrent execution races and unsafe teardown Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:42 ` sashiko-bot
2026-07-13 12:42 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 09/19] media: meson: vdec: Fix vp9 header update failure on invalid payloads Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:42 ` sashiko-bot
2026-07-13 12:42 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 10/19] media: meson: vdec: Fix race conditions and leaks in esparser pipeline Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:46 ` sashiko-bot
2026-07-13 12:46 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 11/19] media: meson: vdec: Update core m2m stream state during transitions Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:48 ` sashiko-bot
2026-07-13 12:48 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 12/19] media: meson: vdec: Coordinate m2m task execution inside async loop Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:54 ` sashiko-bot
2026-07-13 12:54 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 13/19] media: meson: vdec: Fix race conditions in job abort sequence Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:55 ` sashiko-bot
2026-07-13 12:55 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 14/19] media: meson: vdec: Correct atomic counter placement in dst_buf_done Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:48 ` sashiko-bot
2026-07-13 12:48 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 15/19] media: meson: vdec: Fix concurrent firmware loading race and hardware timeout Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:56 ` sashiko-bot
2026-07-13 12:56 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 16/19] media: meson: vdec: Configure DMA mask and segment size in probe Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:55 ` sashiko-bot
2026-07-13 12:55 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 17/19] media: meson: canvas: Fix Use-After-Free by linking canvas provider device Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:58 ` sashiko-bot
2026-07-13 12:58 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 18/19] media: meson: vdec: Increase VIFIFO buffer size to 32 MiB Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 13:06 ` sashiko-bot
2026-07-13 13:06 ` sashiko-bot
2026-07-13 12:07 ` [PATCH v7 19/19] gpu: drm: meson: Fix DMA segment size limits and maximize allocation boundaries Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:07 ` Anand Moon
2026-07-13 12:57 ` sashiko-bot
2026-07-13 12:57 ` sashiko-bot
2026-07-13 14:04 ` Nicolas Dufresne
2026-07-13 14:04 ` Nicolas Dufresne
2026-07-14 7:23 ` [PATCH v7 00/19] media: meson: vdec: Fix lifecycles, race conditions, and stability bugs Anand Moon
2026-07-14 7:23 ` Anand Moon
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.