* [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
@ 2026-07-13 18:16 ` Alfie Varghese
0 siblings, 0 replies; 11+ messages in thread
From: Alfie Varghese @ 2026-07-13 18:16 UTC (permalink / raw)
To: neil.armstrong, gregkh, linux-media, linux-amlogic, linux-staging,
linux-kernel
Cc: Alfie Varghese
This commit fixes several deep concurrency and race condition issues identified
in the Amlogic video decoder driver:
1. Fix a Use-After-Free window in vdec_close() by moving
cancel_work_sync(&sess->esparser_queue_work) to execute after the hardware
is stopped via v4l2_m2m_ctx_release(), preventing interrupts from re-arming it.
2. Fix a Use-After-Free relating to v4l2_m2m_release(). The driver incorrectly
allocated the m2m_dev per-session rather than per-device. Moved the
m2m_dev allocation to vdec_probe() and cleanup to vdec_remove() to fix
the structural UAF when background m2m jobs were pending.
3. Fix a TOCTOU race condition in vdec_isr() by adding synchronize_irq()
in vdec_stop_streaming() after clearing core->cur_sess, ensuring that
running interrupt handlers finish before freeing session data.
4. Fix a potential NULL/ERR pointer dereference in vdec_start_streaming()
by checking the return value of kthread_run() and handling errors gracefully.
5. Fix a memory leak in vdec_close() by freeing the session control
handler via v4l2_ctrl_handler_free().
6. Prevent a potential NULL pointer dereference in vdec_probe() by adding
a NULL check for of_match_node().
Signed-off-by: Alfie Varghese <alfievarghese22@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 52 ++-
drivers/staging/media/meson/vdec/vdec.h | 586 ++++++++++++------------
2 files changed, 331 insertions(+), 307 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index a039d925c0fe..f57c473da5e1 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -337,9 +337,16 @@ 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 = kthread_run(vdec_recycle_thread, sess,
"vdec_recycle");
+ if (IS_ERR(sess->recycle_thread)) {
+ ret = PTR_ERR(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ vdec_poweroff(sess);
+ goto vififo_free;
+ }
+ }
sess->status = STATUS_INIT;
core->cur_sess = sess;
@@ -404,7 +411,7 @@ static void vdec_stop_streaming(struct vb2_queue *q)
sess->status == STATUS_INIT ||
(sess->status == STATUS_NEEDS_RESUME &&
(!sess->streamon_out || !sess->streamon_cap))) {
- if (vdec_codec_needs_recycle(sess))
+ if (vdec_codec_needs_recycle(sess) && sess->recycle_thread)
kthread_stop(sess->recycle_thread);
vdec_poweroff(sess);
@@ -416,6 +423,7 @@ static void vdec_stop_streaming(struct vb2_queue *q)
kfree(sess->priv);
sess->priv = NULL;
core->cur_sess = NULL;
+ synchronize_irq(core->irq);
sess->status = STATUS_STOPPED;
}
@@ -873,18 +881,11 @@ 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);
@@ -915,8 +916,6 @@ static int vdec_open(struct file *file)
err_m2m_ctx_release:
v4l2_m2m_ctx_release(sess->m2m_ctx);
-err_m2m_release:
- v4l2_m2m_release(sess->m2m_dev);
err_free_sess:
kfree(sess);
return ret;
@@ -927,10 +926,12 @@ 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);
+ cancel_work_sync(&sess->esparser_queue_work);
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);
@@ -953,6 +954,9 @@ static irqreturn_t vdec_isr(int irq, void *data)
struct amvdec_core *core = data;
struct amvdec_session *sess = core->cur_sess;
+ if (!sess)
+ return IRQ_NONE;
+
sess->last_irq_jiffies = get_jiffies_64();
return sess->fmt_out->codec_ops->isr(sess);
@@ -963,6 +967,9 @@ static irqreturn_t vdec_threaded_isr(int irq, void *data)
struct amvdec_core *core = data;
struct amvdec_session *sess = core->cur_sess;
+ if (!sess)
+ return IRQ_NONE;
+
return sess->fmt_out->codec_ops->threaded_isr(sess);
}
@@ -1020,6 +1027,8 @@ static int vdec_probe(struct platform_device *pdev)
return PTR_ERR(core->canvas);
of_id = of_match_node(vdec_dt_match, dev->of_node);
+ if (!of_id)
+ return -ENODEV;
core->platform = of_id->data;
if (core->platform->revision == VDEC_REVISION_G12A ||
@@ -1049,6 +1058,8 @@ static int vdec_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ core->irq = irq;
+
ret = devm_request_threaded_irq(core->dev, irq, vdec_isr,
vdec_threaded_isr, IRQF_ONESHOT,
"vdec", core);
@@ -1065,10 +1076,17 @@ static int vdec_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ core->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
+ if (IS_ERR(core->m2m_dev)) {
+ dev_err(dev, "Failed to init v4l2 m2m dev\n");
+ ret = PTR_ERR(core->m2m_dev);
+ goto err_v4l2_unreg;
+ }
+
vdev = video_device_alloc();
if (!vdev) {
ret = -ENOMEM;
- goto err_vdev_release;
+ goto err_m2m_release;
}
core->vdev_dec = vdev;
@@ -1096,6 +1114,9 @@ static int vdec_probe(struct platform_device *pdev)
err_vdev_release:
video_device_release(vdev);
+err_m2m_release:
+ v4l2_m2m_release(core->m2m_dev);
+err_v4l2_unreg:
v4l2_device_unregister(&core->v4l2_dev);
return ret;
}
@@ -1105,6 +1126,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 7a5d8e871d70..b2a606e965b4 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -1,292 +1,294 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * Copyright (C) 2018 BayLibre, SAS
- * Author: Maxime Jourdan <mjourdan@baylibre.com>
- */
-
-#ifndef __MESON_VDEC_CORE_H_
-#define __MESON_VDEC_CORE_H_
-
-#include <linux/irqreturn.h>
-#include <linux/regmap.h>
-#include <linux/list.h>
-#include <media/videobuf2-v4l2.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <linux/soc/amlogic/meson-canvas.h>
-
-#include "vdec_platform.h"
-
-/* 32 buffers in 3-plane YUV420 */
-#define MAX_CANVAS (32 * 3)
-
-struct amvdec_buffer {
- struct list_head list;
- struct vb2_buffer *vb;
-};
-
-/**
- * struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset
- *
- * @list: used to make lists out of this struct
- * @tc: timecode from the v4l2 buffer
- * @ts: timestamp from the VB2 buffer
- * @offset: offset in the VIFIFO where the associated packet was written
- * @flags: flags from the v4l2 buffer
- * @used_count: times this timestamp was checked for a match with a dst buffer
- */
-struct amvdec_timestamp {
- struct list_head list;
- struct v4l2_timecode tc;
- u64 ts;
- u32 offset;
- u32 flags;
- u32 used_count;
-};
-
-struct amvdec_session;
-
-/**
- * struct amvdec_core - device parameters, singleton
- *
- * @dos_base: DOS memory base address
- * @esparser_base: PARSER memory base address
- * @regmap_ao: regmap for the AO bus
- * @dev: core device
- * @dev_dec: decoder device
- * @platform: platform-specific data
- * @canvas: canvas provider reference
- * @dos_parser_clk: DOS_PARSER clock
- * @dos_clk: DOS clock
- * @vdec_1_clk: VDEC_1 clock
- * @vdec_hevc_clk: VDEC_HEVC clock
- * @vdec_hevcf_clk: VDEC_HEVCF clock
- * @esparser_reset: RESET for the PARSER
- * @vdev_dec: video device for the decoder
- * @v4l2_dev: v4l2 device
- * @cur_sess: current decoding session
- * @lock: video device lock
- */
-struct amvdec_core {
- void __iomem *dos_base;
- void __iomem *esparser_base;
- struct regmap *regmap_ao;
-
- struct device *dev;
- struct device *dev_dec;
- const struct vdec_platform *platform;
-
- struct meson_canvas *canvas;
-
- struct clk *dos_parser_clk;
- struct clk *dos_clk;
- struct clk *vdec_1_clk;
- struct clk *vdec_hevc_clk;
- struct clk *vdec_hevcf_clk;
-
- struct reset_control *esparser_reset;
-
- struct video_device *vdev_dec;
- struct v4l2_device v4l2_dev;
-
- struct amvdec_session *cur_sess;
- struct mutex lock;
-};
-
-/**
- * struct amvdec_ops - vdec operations
- *
- * @start: mandatory call when the vdec needs to initialize
- * @stop: mandatory call when the vdec needs to stop
- * @conf_esparser: mandatory call to let the vdec configure the ESPARSER
- * @vififo_level: mandatory call to get the current amount of data
- * in the VIFIFO
- */
-struct amvdec_ops {
- int (*start)(struct amvdec_session *sess);
- int (*stop)(struct amvdec_session *sess);
- void (*conf_esparser)(struct amvdec_session *sess);
- u32 (*vififo_level)(struct amvdec_session *sess);
-};
-
-/**
- * struct amvdec_codec_ops - codec operations
- *
- * @start: mandatory call when the codec needs to initialize
- * @stop: mandatory call when the codec needs to stop
- * @load_extended_firmware: optional call to load additional firmware bits
- * @num_pending_bufs: optional call to get the number of dst buffers on hold
- * @can_recycle: optional call to know if the codec is ready to recycle
- * a dst buffer
- * @recycle: optional call to tell the codec to recycle a dst buffer. Must go
- * in pair with @can_recycle
- * @drain: optional call if the codec has a custom way of draining
- * @resume: optional call to resume after a resolution change
- * @eos_sequence: optional call to get an end sequence to send to esparser
- * for flush. Mutually exclusive with @drain.
- * @isr: mandatory call when the ISR triggers
- * @threaded_isr: mandatory call for the threaded ISR
- */
-struct amvdec_codec_ops {
- int (*start)(struct amvdec_session *sess);
- int (*stop)(struct amvdec_session *sess);
- int (*load_extended_firmware)(struct amvdec_session *sess,
- const u8 *data, u32 len);
- u32 (*num_pending_bufs)(struct amvdec_session *sess);
- int (*can_recycle)(struct amvdec_core *core);
- void (*recycle)(struct amvdec_core *core, u32 buf_idx);
- void (*drain)(struct amvdec_session *sess);
- void (*resume)(struct amvdec_session *sess);
- const u8 * (*eos_sequence)(u32 *len);
- irqreturn_t (*isr)(struct amvdec_session *sess);
- irqreturn_t (*threaded_isr)(struct amvdec_session *sess);
-};
-
-/**
- * struct amvdec_format - describes one of the OUTPUT (src) format supported
- *
- * @pixfmt: V4L2 pixel format
- * @min_buffers: minimum amount of CAPTURE (dst) buffers
- * @max_buffers: maximum amount of CAPTURE (dst) buffers
- * @max_width: maximum picture width supported
- * @max_height: maximum picture height supported
- * @flags: enum flags associated with this pixfmt
- * @vdec_ops: the VDEC operations that support this format
- * @codec_ops: the codec operations that support this format
- * @firmware_path: Path to the firmware that supports this format
- * @pixfmts_cap: list of CAPTURE pixel formats available with pixfmt
- */
-struct amvdec_format {
- u32 pixfmt;
- u32 min_buffers;
- u32 max_buffers;
- u32 max_width;
- u32 max_height;
- u32 flags;
-
- struct amvdec_ops *vdec_ops;
- struct amvdec_codec_ops *codec_ops;
-
- char *firmware_path;
- u32 pixfmts_cap[4];
-};
-
-enum amvdec_status {
- STATUS_STOPPED,
- STATUS_INIT,
- STATUS_RUNNING,
- STATUS_NEEDS_RESUME,
-};
-
-/**
- * struct amvdec_session - decoding session parameters
- *
- * @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
- * @lock: cap & out queues lock
- * @fmt_out: vdec pixel format for the OUTPUT queue
- * @pixfmt_cap: V4L2 pixel format for the CAPTURE queue
- * @src_buffer_size: size in bytes of the OUTPUT buffers' only plane
- * @width: current picture width
- * @height: current picture height
- * @colorspace: current colorspace
- * @ycbcr_enc: current ycbcr_enc
- * @quantization: current quantization
- * @xfer_func: current transfer function
- * @pixelaspect: Pixel Aspect Ratio reported by the decoder
- * @esparser_queued_bufs: number of buffers currently queued into ESPARSER
- * @esparser_queue_work: work struct for the ESPARSER to process src buffers
- * @streamon_cap: stream on flag for capture queue
- * @streamon_out: stream on flag for output queue
- * @sequence_cap: capture sequence counter
- * @sequence_out: output sequence counter
- * @should_stop: flag set if userspace signaled EOS via command
- * or empty buffer
- * @keyframe_found: flag set once a keyframe has been parsed
- * @num_dst_bufs: number of destination buffers
- * @changed_format: the format changed
- * @canvas_alloc: array of all the canvas IDs allocated
- * @canvas_num: number of canvas IDs allocated
- * @vififo_vaddr: virtual address for the VIFIFO
- * @vififo_paddr: physical address for the VIFIFO
- * @vififo_size: size of the VIFIFO dma alloc
- * @bufs_recycle: list of buffers that need to be recycled
- * @bufs_recycle_lock: lock for the bufs_recycle list
- * @recycle_thread: task struct for the recycling thread
- * @timestamps: chronological list of src timestamps
- * @ts_spinlock: spinlock for the timestamps list
- * @last_irq_jiffies: tracks last time the vdec triggered an IRQ
- * @last_offset: tracks last offset of vififo
- * @wrap_count: number of times the vififo wrapped around
- * @fw_idx_to_vb2_idx: firmware buffer index to vb2 buffer index
- * @status: current decoding status
- * @priv: codec private data
- */
-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;
- struct mutex lock;
-
- const struct amvdec_format *fmt_out;
- u32 pixfmt_cap;
- u32 src_buffer_size;
-
- u32 width;
- u32 height;
- u32 colorspace;
- u8 ycbcr_enc;
- u8 quantization;
- u8 xfer_func;
-
- struct v4l2_fract pixelaspect;
-
- atomic_t esparser_queued_bufs;
- struct work_struct esparser_queue_work;
-
- unsigned int streamon_cap, streamon_out;
- unsigned int sequence_cap, sequence_out;
- unsigned int should_stop;
- unsigned int keyframe_found;
- unsigned int num_dst_bufs;
- unsigned int changed_format;
-
- u8 canvas_alloc[MAX_CANVAS];
- u32 canvas_num;
-
- void *vififo_vaddr;
- dma_addr_t vififo_paddr;
- u32 vififo_size;
-
- struct list_head bufs_recycle;
- struct mutex bufs_recycle_lock; /* bufs_recycle list lock */
- struct task_struct *recycle_thread;
-
- struct list_head timestamps;
- spinlock_t ts_spinlock; /* timestamp list lock */
-
- u64 last_irq_jiffies;
- u32 last_offset;
- u32 wrap_count;
- u32 fw_idx_to_vb2_idx[32];
-
- enum amvdec_status status;
- void *priv;
-};
-
-static inline struct amvdec_session *file_to_amvdec_session(struct file *filp)
-{
- return container_of(file_to_v4l2_fh(filp), struct amvdec_session, fh);
-}
-
-u32 amvdec_get_output_size(struct amvdec_session *sess);
-
-#endif
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018 BayLibre, SAS
+ * Author: Maxime Jourdan <mjourdan@baylibre.com>
+ */
+
+#ifndef __MESON_VDEC_CORE_H_
+#define __MESON_VDEC_CORE_H_
+
+#include <linux/irqreturn.h>
+#include <linux/regmap.h>
+#include <linux/list.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <linux/soc/amlogic/meson-canvas.h>
+
+#include "vdec_platform.h"
+
+/* 32 buffers in 3-plane YUV420 */
+#define MAX_CANVAS (32 * 3)
+
+struct amvdec_buffer {
+ struct list_head list;
+ struct vb2_buffer *vb;
+};
+
+/**
+ * struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset
+ *
+ * @list: used to make lists out of this struct
+ * @tc: timecode from the v4l2 buffer
+ * @ts: timestamp from the VB2 buffer
+ * @offset: offset in the VIFIFO where the associated packet was written
+ * @flags: flags from the v4l2 buffer
+ * @used_count: times this timestamp was checked for a match with a dst buffer
+ */
+struct amvdec_timestamp {
+ struct list_head list;
+ struct v4l2_timecode tc;
+ u64 ts;
+ u32 offset;
+ u32 flags;
+ u32 used_count;
+};
+
+struct amvdec_session;
+
+/**
+ * struct amvdec_core - device parameters, singleton
+ *
+ * @dos_base: DOS memory base address
+ * @esparser_base: PARSER memory base address
+ * @regmap_ao: regmap for the AO bus
+ * @dev: core device
+ * @dev_dec: decoder device
+ * @platform: platform-specific data
+ * @canvas: canvas provider reference
+ * @dos_parser_clk: DOS_PARSER clock
+ * @dos_clk: DOS clock
+ * @vdec_1_clk: VDEC_1 clock
+ * @vdec_hevc_clk: VDEC_HEVC clock
+ * @vdec_hevcf_clk: VDEC_HEVCF clock
+ * @esparser_reset: RESET for the PARSER
+ * @vdev_dec: video device for the decoder
+ * @v4l2_dev: v4l2 device
+ * @m2m_dev: v4l2 m2m device
+ * @cur_sess: current decoding session
+ * @lock: video device lock
+ * @irq: decoder irq
+ */
+struct amvdec_core {
+ void __iomem *dos_base;
+ void __iomem *esparser_base;
+ struct regmap *regmap_ao;
+
+ struct device *dev;
+ struct device *dev_dec;
+ const struct vdec_platform *platform;
+
+ struct meson_canvas *canvas;
+
+ struct clk *dos_parser_clk;
+ struct clk *dos_clk;
+ struct clk *vdec_1_clk;
+ struct clk *vdec_hevc_clk;
+ struct clk *vdec_hevcf_clk;
+
+ struct reset_control *esparser_reset;
+
+ struct video_device *vdev_dec;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *m2m_dev;
+
+ struct amvdec_session *cur_sess;
+ struct mutex lock;
+ int irq;
+};
+
+/**
+ * struct amvdec_ops - vdec operations
+ *
+ * @start: mandatory call when the vdec needs to initialize
+ * @stop: mandatory call when the vdec needs to stop
+ * @conf_esparser: mandatory call to let the vdec configure the ESPARSER
+ * @vififo_level: mandatory call to get the current amount of data
+ * in the VIFIFO
+ */
+struct amvdec_ops {
+ int (*start)(struct amvdec_session *sess);
+ int (*stop)(struct amvdec_session *sess);
+ void (*conf_esparser)(struct amvdec_session *sess);
+ u32 (*vififo_level)(struct amvdec_session *sess);
+};
+
+/**
+ * struct amvdec_codec_ops - codec operations
+ *
+ * @start: mandatory call when the codec needs to initialize
+ * @stop: mandatory call when the codec needs to stop
+ * @load_extended_firmware: optional call to load additional firmware bits
+ * @num_pending_bufs: optional call to get the number of dst buffers on hold
+ * @can_recycle: optional call to know if the codec is ready to recycle
+ * a dst buffer
+ * @recycle: optional call to tell the codec to recycle a dst buffer. Must go
+ * in pair with @can_recycle
+ * @drain: optional call if the codec has a custom way of draining
+ * @resume: optional call to resume after a resolution change
+ * @eos_sequence: optional call to get an end sequence to send to esparser
+ * for flush. Mutually exclusive with @drain.
+ * @isr: mandatory call when the ISR triggers
+ * @threaded_isr: mandatory call for the threaded ISR
+ */
+struct amvdec_codec_ops {
+ int (*start)(struct amvdec_session *sess);
+ int (*stop)(struct amvdec_session *sess);
+ int (*load_extended_firmware)(struct amvdec_session *sess,
+ const u8 *data, u32 len);
+ u32 (*num_pending_bufs)(struct amvdec_session *sess);
+ int (*can_recycle)(struct amvdec_core *core);
+ void (*recycle)(struct amvdec_core *core, u32 buf_idx);
+ void (*drain)(struct amvdec_session *sess);
+ void (*resume)(struct amvdec_session *sess);
+ const u8 * (*eos_sequence)(u32 *len);
+ irqreturn_t (*isr)(struct amvdec_session *sess);
+ irqreturn_t (*threaded_isr)(struct amvdec_session *sess);
+};
+
+/**
+ * struct amvdec_format - describes one of the OUTPUT (src) format supported
+ *
+ * @pixfmt: V4L2 pixel format
+ * @min_buffers: minimum amount of CAPTURE (dst) buffers
+ * @max_buffers: maximum amount of CAPTURE (dst) buffers
+ * @max_width: maximum picture width supported
+ * @max_height: maximum picture height supported
+ * @flags: enum flags associated with this pixfmt
+ * @vdec_ops: the VDEC operations that support this format
+ * @codec_ops: the codec operations that support this format
+ * @firmware_path: Path to the firmware that supports this format
+ * @pixfmts_cap: list of CAPTURE pixel formats available with pixfmt
+ */
+struct amvdec_format {
+ u32 pixfmt;
+ u32 min_buffers;
+ u32 max_buffers;
+ u32 max_width;
+ u32 max_height;
+ u32 flags;
+
+ struct amvdec_ops *vdec_ops;
+ struct amvdec_codec_ops *codec_ops;
+
+ char *firmware_path;
+ u32 pixfmts_cap[4];
+};
+
+enum amvdec_status {
+ STATUS_STOPPED,
+ STATUS_INIT,
+ STATUS_RUNNING,
+ STATUS_NEEDS_RESUME,
+};
+
+/**
+ * struct amvdec_session - decoding session parameters
+ *
+ * @core: reference to the vdec core struct
+ * @fh: v4l2 file handle
+ * @m2m_ctx: v4l2 m2m context
+ * @ctrl_handler: V4L2 control handler
+ * @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE
+ * @lock: cap & out queues lock
+ * @fmt_out: vdec pixel format for the OUTPUT queue
+ * @pixfmt_cap: V4L2 pixel format for the CAPTURE queue
+ * @src_buffer_size: size in bytes of the OUTPUT buffers' only plane
+ * @width: current picture width
+ * @height: current picture height
+ * @colorspace: current colorspace
+ * @ycbcr_enc: current ycbcr_enc
+ * @quantization: current quantization
+ * @xfer_func: current transfer function
+ * @pixelaspect: Pixel Aspect Ratio reported by the decoder
+ * @esparser_queued_bufs: number of buffers currently queued into ESPARSER
+ * @esparser_queue_work: work struct for the ESPARSER to process src buffers
+ * @streamon_cap: stream on flag for capture queue
+ * @streamon_out: stream on flag for output queue
+ * @sequence_cap: capture sequence counter
+ * @sequence_out: output sequence counter
+ * @should_stop: flag set if userspace signaled EOS via command
+ * or empty buffer
+ * @keyframe_found: flag set once a keyframe has been parsed
+ * @num_dst_bufs: number of destination buffers
+ * @changed_format: the format changed
+ * @canvas_alloc: array of all the canvas IDs allocated
+ * @canvas_num: number of canvas IDs allocated
+ * @vififo_vaddr: virtual address for the VIFIFO
+ * @vififo_paddr: physical address for the VIFIFO
+ * @vififo_size: size of the VIFIFO dma alloc
+ * @bufs_recycle: list of buffers that need to be recycled
+ * @bufs_recycle_lock: lock for the bufs_recycle list
+ * @recycle_thread: task struct for the recycling thread
+ * @timestamps: chronological list of src timestamps
+ * @ts_spinlock: spinlock for the timestamps list
+ * @last_irq_jiffies: tracks last time the vdec triggered an IRQ
+ * @last_offset: tracks last offset of vififo
+ * @wrap_count: number of times the vififo wrapped around
+ * @fw_idx_to_vb2_idx: firmware buffer index to vb2 buffer index
+ * @status: current decoding status
+ * @priv: codec private data
+ */
+struct amvdec_session {
+ struct amvdec_core *core;
+
+ struct v4l2_fh fh;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *ctrl_min_buf_capture;
+ struct mutex lock;
+
+ const struct amvdec_format *fmt_out;
+ u32 pixfmt_cap;
+ u32 src_buffer_size;
+
+ u32 width;
+ u32 height;
+ u32 colorspace;
+ u8 ycbcr_enc;
+ u8 quantization;
+ u8 xfer_func;
+
+ struct v4l2_fract pixelaspect;
+
+ atomic_t esparser_queued_bufs;
+ struct work_struct esparser_queue_work;
+
+ unsigned int streamon_cap, streamon_out;
+ unsigned int sequence_cap, sequence_out;
+ unsigned int should_stop;
+ unsigned int keyframe_found;
+ unsigned int num_dst_bufs;
+ unsigned int changed_format;
+
+ u8 canvas_alloc[MAX_CANVAS];
+ u32 canvas_num;
+
+ void *vififo_vaddr;
+ dma_addr_t vififo_paddr;
+ u32 vififo_size;
+
+ struct list_head bufs_recycle;
+ struct mutex bufs_recycle_lock; /* bufs_recycle list lock */
+ struct task_struct *recycle_thread;
+
+ struct list_head timestamps;
+ spinlock_t ts_spinlock; /* timestamp list lock */
+
+ u64 last_irq_jiffies;
+ u32 last_offset;
+ u32 wrap_count;
+ u32 fw_idx_to_vb2_idx[32];
+
+ enum amvdec_status status;
+ void *priv;
+};
+
+static inline struct amvdec_session *file_to_amvdec_session(struct file *filp)
+{
+ return container_of(file_to_v4l2_fh(filp), struct amvdec_session, fh);
+}
+
+u32 amvdec_get_output_size(struct amvdec_session *sess);
+
+#endif
--
2.54.0.windows.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
@ 2026-07-13 18:16 ` Alfie Varghese
0 siblings, 0 replies; 11+ messages in thread
From: Alfie Varghese @ 2026-07-13 18:16 UTC (permalink / raw)
To: neil.armstrong, gregkh, linux-media, linux-amlogic, linux-staging,
linux-kernel
Cc: Alfie Varghese
This commit fixes several deep concurrency and race condition issues identified
in the Amlogic video decoder driver:
1. Fix a Use-After-Free window in vdec_close() by moving
cancel_work_sync(&sess->esparser_queue_work) to execute after the hardware
is stopped via v4l2_m2m_ctx_release(), preventing interrupts from re-arming it.
2. Fix a Use-After-Free relating to v4l2_m2m_release(). The driver incorrectly
allocated the m2m_dev per-session rather than per-device. Moved the
m2m_dev allocation to vdec_probe() and cleanup to vdec_remove() to fix
the structural UAF when background m2m jobs were pending.
3. Fix a TOCTOU race condition in vdec_isr() by adding synchronize_irq()
in vdec_stop_streaming() after clearing core->cur_sess, ensuring that
running interrupt handlers finish before freeing session data.
4. Fix a potential NULL/ERR pointer dereference in vdec_start_streaming()
by checking the return value of kthread_run() and handling errors gracefully.
5. Fix a memory leak in vdec_close() by freeing the session control
handler via v4l2_ctrl_handler_free().
6. Prevent a potential NULL pointer dereference in vdec_probe() by adding
a NULL check for of_match_node().
Signed-off-by: Alfie Varghese <alfievarghese22@gmail.com>
---
drivers/staging/media/meson/vdec/vdec.c | 52 ++-
drivers/staging/media/meson/vdec/vdec.h | 586 ++++++++++++------------
2 files changed, 331 insertions(+), 307 deletions(-)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index a039d925c0fe..f57c473da5e1 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -337,9 +337,16 @@ 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 = kthread_run(vdec_recycle_thread, sess,
"vdec_recycle");
+ if (IS_ERR(sess->recycle_thread)) {
+ ret = PTR_ERR(sess->recycle_thread);
+ sess->recycle_thread = NULL;
+ vdec_poweroff(sess);
+ goto vififo_free;
+ }
+ }
sess->status = STATUS_INIT;
core->cur_sess = sess;
@@ -404,7 +411,7 @@ static void vdec_stop_streaming(struct vb2_queue *q)
sess->status == STATUS_INIT ||
(sess->status == STATUS_NEEDS_RESUME &&
(!sess->streamon_out || !sess->streamon_cap))) {
- if (vdec_codec_needs_recycle(sess))
+ if (vdec_codec_needs_recycle(sess) && sess->recycle_thread)
kthread_stop(sess->recycle_thread);
vdec_poweroff(sess);
@@ -416,6 +423,7 @@ static void vdec_stop_streaming(struct vb2_queue *q)
kfree(sess->priv);
sess->priv = NULL;
core->cur_sess = NULL;
+ synchronize_irq(core->irq);
sess->status = STATUS_STOPPED;
}
@@ -873,18 +881,11 @@ 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);
@@ -915,8 +916,6 @@ static int vdec_open(struct file *file)
err_m2m_ctx_release:
v4l2_m2m_ctx_release(sess->m2m_ctx);
-err_m2m_release:
- v4l2_m2m_release(sess->m2m_dev);
err_free_sess:
kfree(sess);
return ret;
@@ -927,10 +926,12 @@ 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);
+ cancel_work_sync(&sess->esparser_queue_work);
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);
@@ -953,6 +954,9 @@ static irqreturn_t vdec_isr(int irq, void *data)
struct amvdec_core *core = data;
struct amvdec_session *sess = core->cur_sess;
+ if (!sess)
+ return IRQ_NONE;
+
sess->last_irq_jiffies = get_jiffies_64();
return sess->fmt_out->codec_ops->isr(sess);
@@ -963,6 +967,9 @@ static irqreturn_t vdec_threaded_isr(int irq, void *data)
struct amvdec_core *core = data;
struct amvdec_session *sess = core->cur_sess;
+ if (!sess)
+ return IRQ_NONE;
+
return sess->fmt_out->codec_ops->threaded_isr(sess);
}
@@ -1020,6 +1027,8 @@ static int vdec_probe(struct platform_device *pdev)
return PTR_ERR(core->canvas);
of_id = of_match_node(vdec_dt_match, dev->of_node);
+ if (!of_id)
+ return -ENODEV;
core->platform = of_id->data;
if (core->platform->revision == VDEC_REVISION_G12A ||
@@ -1049,6 +1058,8 @@ static int vdec_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ core->irq = irq;
+
ret = devm_request_threaded_irq(core->dev, irq, vdec_isr,
vdec_threaded_isr, IRQF_ONESHOT,
"vdec", core);
@@ -1065,10 +1076,17 @@ static int vdec_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ core->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
+ if (IS_ERR(core->m2m_dev)) {
+ dev_err(dev, "Failed to init v4l2 m2m dev\n");
+ ret = PTR_ERR(core->m2m_dev);
+ goto err_v4l2_unreg;
+ }
+
vdev = video_device_alloc();
if (!vdev) {
ret = -ENOMEM;
- goto err_vdev_release;
+ goto err_m2m_release;
}
core->vdev_dec = vdev;
@@ -1096,6 +1114,9 @@ static int vdec_probe(struct platform_device *pdev)
err_vdev_release:
video_device_release(vdev);
+err_m2m_release:
+ v4l2_m2m_release(core->m2m_dev);
+err_v4l2_unreg:
v4l2_device_unregister(&core->v4l2_dev);
return ret;
}
@@ -1105,6 +1126,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 7a5d8e871d70..b2a606e965b4 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -1,292 +1,294 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * Copyright (C) 2018 BayLibre, SAS
- * Author: Maxime Jourdan <mjourdan@baylibre.com>
- */
-
-#ifndef __MESON_VDEC_CORE_H_
-#define __MESON_VDEC_CORE_H_
-
-#include <linux/irqreturn.h>
-#include <linux/regmap.h>
-#include <linux/list.h>
-#include <media/videobuf2-v4l2.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <linux/soc/amlogic/meson-canvas.h>
-
-#include "vdec_platform.h"
-
-/* 32 buffers in 3-plane YUV420 */
-#define MAX_CANVAS (32 * 3)
-
-struct amvdec_buffer {
- struct list_head list;
- struct vb2_buffer *vb;
-};
-
-/**
- * struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset
- *
- * @list: used to make lists out of this struct
- * @tc: timecode from the v4l2 buffer
- * @ts: timestamp from the VB2 buffer
- * @offset: offset in the VIFIFO where the associated packet was written
- * @flags: flags from the v4l2 buffer
- * @used_count: times this timestamp was checked for a match with a dst buffer
- */
-struct amvdec_timestamp {
- struct list_head list;
- struct v4l2_timecode tc;
- u64 ts;
- u32 offset;
- u32 flags;
- u32 used_count;
-};
-
-struct amvdec_session;
-
-/**
- * struct amvdec_core - device parameters, singleton
- *
- * @dos_base: DOS memory base address
- * @esparser_base: PARSER memory base address
- * @regmap_ao: regmap for the AO bus
- * @dev: core device
- * @dev_dec: decoder device
- * @platform: platform-specific data
- * @canvas: canvas provider reference
- * @dos_parser_clk: DOS_PARSER clock
- * @dos_clk: DOS clock
- * @vdec_1_clk: VDEC_1 clock
- * @vdec_hevc_clk: VDEC_HEVC clock
- * @vdec_hevcf_clk: VDEC_HEVCF clock
- * @esparser_reset: RESET for the PARSER
- * @vdev_dec: video device for the decoder
- * @v4l2_dev: v4l2 device
- * @cur_sess: current decoding session
- * @lock: video device lock
- */
-struct amvdec_core {
- void __iomem *dos_base;
- void __iomem *esparser_base;
- struct regmap *regmap_ao;
-
- struct device *dev;
- struct device *dev_dec;
- const struct vdec_platform *platform;
-
- struct meson_canvas *canvas;
-
- struct clk *dos_parser_clk;
- struct clk *dos_clk;
- struct clk *vdec_1_clk;
- struct clk *vdec_hevc_clk;
- struct clk *vdec_hevcf_clk;
-
- struct reset_control *esparser_reset;
-
- struct video_device *vdev_dec;
- struct v4l2_device v4l2_dev;
-
- struct amvdec_session *cur_sess;
- struct mutex lock;
-};
-
-/**
- * struct amvdec_ops - vdec operations
- *
- * @start: mandatory call when the vdec needs to initialize
- * @stop: mandatory call when the vdec needs to stop
- * @conf_esparser: mandatory call to let the vdec configure the ESPARSER
- * @vififo_level: mandatory call to get the current amount of data
- * in the VIFIFO
- */
-struct amvdec_ops {
- int (*start)(struct amvdec_session *sess);
- int (*stop)(struct amvdec_session *sess);
- void (*conf_esparser)(struct amvdec_session *sess);
- u32 (*vififo_level)(struct amvdec_session *sess);
-};
-
-/**
- * struct amvdec_codec_ops - codec operations
- *
- * @start: mandatory call when the codec needs to initialize
- * @stop: mandatory call when the codec needs to stop
- * @load_extended_firmware: optional call to load additional firmware bits
- * @num_pending_bufs: optional call to get the number of dst buffers on hold
- * @can_recycle: optional call to know if the codec is ready to recycle
- * a dst buffer
- * @recycle: optional call to tell the codec to recycle a dst buffer. Must go
- * in pair with @can_recycle
- * @drain: optional call if the codec has a custom way of draining
- * @resume: optional call to resume after a resolution change
- * @eos_sequence: optional call to get an end sequence to send to esparser
- * for flush. Mutually exclusive with @drain.
- * @isr: mandatory call when the ISR triggers
- * @threaded_isr: mandatory call for the threaded ISR
- */
-struct amvdec_codec_ops {
- int (*start)(struct amvdec_session *sess);
- int (*stop)(struct amvdec_session *sess);
- int (*load_extended_firmware)(struct amvdec_session *sess,
- const u8 *data, u32 len);
- u32 (*num_pending_bufs)(struct amvdec_session *sess);
- int (*can_recycle)(struct amvdec_core *core);
- void (*recycle)(struct amvdec_core *core, u32 buf_idx);
- void (*drain)(struct amvdec_session *sess);
- void (*resume)(struct amvdec_session *sess);
- const u8 * (*eos_sequence)(u32 *len);
- irqreturn_t (*isr)(struct amvdec_session *sess);
- irqreturn_t (*threaded_isr)(struct amvdec_session *sess);
-};
-
-/**
- * struct amvdec_format - describes one of the OUTPUT (src) format supported
- *
- * @pixfmt: V4L2 pixel format
- * @min_buffers: minimum amount of CAPTURE (dst) buffers
- * @max_buffers: maximum amount of CAPTURE (dst) buffers
- * @max_width: maximum picture width supported
- * @max_height: maximum picture height supported
- * @flags: enum flags associated with this pixfmt
- * @vdec_ops: the VDEC operations that support this format
- * @codec_ops: the codec operations that support this format
- * @firmware_path: Path to the firmware that supports this format
- * @pixfmts_cap: list of CAPTURE pixel formats available with pixfmt
- */
-struct amvdec_format {
- u32 pixfmt;
- u32 min_buffers;
- u32 max_buffers;
- u32 max_width;
- u32 max_height;
- u32 flags;
-
- struct amvdec_ops *vdec_ops;
- struct amvdec_codec_ops *codec_ops;
-
- char *firmware_path;
- u32 pixfmts_cap[4];
-};
-
-enum amvdec_status {
- STATUS_STOPPED,
- STATUS_INIT,
- STATUS_RUNNING,
- STATUS_NEEDS_RESUME,
-};
-
-/**
- * struct amvdec_session - decoding session parameters
- *
- * @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
- * @lock: cap & out queues lock
- * @fmt_out: vdec pixel format for the OUTPUT queue
- * @pixfmt_cap: V4L2 pixel format for the CAPTURE queue
- * @src_buffer_size: size in bytes of the OUTPUT buffers' only plane
- * @width: current picture width
- * @height: current picture height
- * @colorspace: current colorspace
- * @ycbcr_enc: current ycbcr_enc
- * @quantization: current quantization
- * @xfer_func: current transfer function
- * @pixelaspect: Pixel Aspect Ratio reported by the decoder
- * @esparser_queued_bufs: number of buffers currently queued into ESPARSER
- * @esparser_queue_work: work struct for the ESPARSER to process src buffers
- * @streamon_cap: stream on flag for capture queue
- * @streamon_out: stream on flag for output queue
- * @sequence_cap: capture sequence counter
- * @sequence_out: output sequence counter
- * @should_stop: flag set if userspace signaled EOS via command
- * or empty buffer
- * @keyframe_found: flag set once a keyframe has been parsed
- * @num_dst_bufs: number of destination buffers
- * @changed_format: the format changed
- * @canvas_alloc: array of all the canvas IDs allocated
- * @canvas_num: number of canvas IDs allocated
- * @vififo_vaddr: virtual address for the VIFIFO
- * @vififo_paddr: physical address for the VIFIFO
- * @vififo_size: size of the VIFIFO dma alloc
- * @bufs_recycle: list of buffers that need to be recycled
- * @bufs_recycle_lock: lock for the bufs_recycle list
- * @recycle_thread: task struct for the recycling thread
- * @timestamps: chronological list of src timestamps
- * @ts_spinlock: spinlock for the timestamps list
- * @last_irq_jiffies: tracks last time the vdec triggered an IRQ
- * @last_offset: tracks last offset of vififo
- * @wrap_count: number of times the vififo wrapped around
- * @fw_idx_to_vb2_idx: firmware buffer index to vb2 buffer index
- * @status: current decoding status
- * @priv: codec private data
- */
-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;
- struct mutex lock;
-
- const struct amvdec_format *fmt_out;
- u32 pixfmt_cap;
- u32 src_buffer_size;
-
- u32 width;
- u32 height;
- u32 colorspace;
- u8 ycbcr_enc;
- u8 quantization;
- u8 xfer_func;
-
- struct v4l2_fract pixelaspect;
-
- atomic_t esparser_queued_bufs;
- struct work_struct esparser_queue_work;
-
- unsigned int streamon_cap, streamon_out;
- unsigned int sequence_cap, sequence_out;
- unsigned int should_stop;
- unsigned int keyframe_found;
- unsigned int num_dst_bufs;
- unsigned int changed_format;
-
- u8 canvas_alloc[MAX_CANVAS];
- u32 canvas_num;
-
- void *vififo_vaddr;
- dma_addr_t vififo_paddr;
- u32 vififo_size;
-
- struct list_head bufs_recycle;
- struct mutex bufs_recycle_lock; /* bufs_recycle list lock */
- struct task_struct *recycle_thread;
-
- struct list_head timestamps;
- spinlock_t ts_spinlock; /* timestamp list lock */
-
- u64 last_irq_jiffies;
- u32 last_offset;
- u32 wrap_count;
- u32 fw_idx_to_vb2_idx[32];
-
- enum amvdec_status status;
- void *priv;
-};
-
-static inline struct amvdec_session *file_to_amvdec_session(struct file *filp)
-{
- return container_of(file_to_v4l2_fh(filp), struct amvdec_session, fh);
-}
-
-u32 amvdec_get_output_size(struct amvdec_session *sess);
-
-#endif
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018 BayLibre, SAS
+ * Author: Maxime Jourdan <mjourdan@baylibre.com>
+ */
+
+#ifndef __MESON_VDEC_CORE_H_
+#define __MESON_VDEC_CORE_H_
+
+#include <linux/irqreturn.h>
+#include <linux/regmap.h>
+#include <linux/list.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <linux/soc/amlogic/meson-canvas.h>
+
+#include "vdec_platform.h"
+
+/* 32 buffers in 3-plane YUV420 */
+#define MAX_CANVAS (32 * 3)
+
+struct amvdec_buffer {
+ struct list_head list;
+ struct vb2_buffer *vb;
+};
+
+/**
+ * struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset
+ *
+ * @list: used to make lists out of this struct
+ * @tc: timecode from the v4l2 buffer
+ * @ts: timestamp from the VB2 buffer
+ * @offset: offset in the VIFIFO where the associated packet was written
+ * @flags: flags from the v4l2 buffer
+ * @used_count: times this timestamp was checked for a match with a dst buffer
+ */
+struct amvdec_timestamp {
+ struct list_head list;
+ struct v4l2_timecode tc;
+ u64 ts;
+ u32 offset;
+ u32 flags;
+ u32 used_count;
+};
+
+struct amvdec_session;
+
+/**
+ * struct amvdec_core - device parameters, singleton
+ *
+ * @dos_base: DOS memory base address
+ * @esparser_base: PARSER memory base address
+ * @regmap_ao: regmap for the AO bus
+ * @dev: core device
+ * @dev_dec: decoder device
+ * @platform: platform-specific data
+ * @canvas: canvas provider reference
+ * @dos_parser_clk: DOS_PARSER clock
+ * @dos_clk: DOS clock
+ * @vdec_1_clk: VDEC_1 clock
+ * @vdec_hevc_clk: VDEC_HEVC clock
+ * @vdec_hevcf_clk: VDEC_HEVCF clock
+ * @esparser_reset: RESET for the PARSER
+ * @vdev_dec: video device for the decoder
+ * @v4l2_dev: v4l2 device
+ * @m2m_dev: v4l2 m2m device
+ * @cur_sess: current decoding session
+ * @lock: video device lock
+ * @irq: decoder irq
+ */
+struct amvdec_core {
+ void __iomem *dos_base;
+ void __iomem *esparser_base;
+ struct regmap *regmap_ao;
+
+ struct device *dev;
+ struct device *dev_dec;
+ const struct vdec_platform *platform;
+
+ struct meson_canvas *canvas;
+
+ struct clk *dos_parser_clk;
+ struct clk *dos_clk;
+ struct clk *vdec_1_clk;
+ struct clk *vdec_hevc_clk;
+ struct clk *vdec_hevcf_clk;
+
+ struct reset_control *esparser_reset;
+
+ struct video_device *vdev_dec;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *m2m_dev;
+
+ struct amvdec_session *cur_sess;
+ struct mutex lock;
+ int irq;
+};
+
+/**
+ * struct amvdec_ops - vdec operations
+ *
+ * @start: mandatory call when the vdec needs to initialize
+ * @stop: mandatory call when the vdec needs to stop
+ * @conf_esparser: mandatory call to let the vdec configure the ESPARSER
+ * @vififo_level: mandatory call to get the current amount of data
+ * in the VIFIFO
+ */
+struct amvdec_ops {
+ int (*start)(struct amvdec_session *sess);
+ int (*stop)(struct amvdec_session *sess);
+ void (*conf_esparser)(struct amvdec_session *sess);
+ u32 (*vififo_level)(struct amvdec_session *sess);
+};
+
+/**
+ * struct amvdec_codec_ops - codec operations
+ *
+ * @start: mandatory call when the codec needs to initialize
+ * @stop: mandatory call when the codec needs to stop
+ * @load_extended_firmware: optional call to load additional firmware bits
+ * @num_pending_bufs: optional call to get the number of dst buffers on hold
+ * @can_recycle: optional call to know if the codec is ready to recycle
+ * a dst buffer
+ * @recycle: optional call to tell the codec to recycle a dst buffer. Must go
+ * in pair with @can_recycle
+ * @drain: optional call if the codec has a custom way of draining
+ * @resume: optional call to resume after a resolution change
+ * @eos_sequence: optional call to get an end sequence to send to esparser
+ * for flush. Mutually exclusive with @drain.
+ * @isr: mandatory call when the ISR triggers
+ * @threaded_isr: mandatory call for the threaded ISR
+ */
+struct amvdec_codec_ops {
+ int (*start)(struct amvdec_session *sess);
+ int (*stop)(struct amvdec_session *sess);
+ int (*load_extended_firmware)(struct amvdec_session *sess,
+ const u8 *data, u32 len);
+ u32 (*num_pending_bufs)(struct amvdec_session *sess);
+ int (*can_recycle)(struct amvdec_core *core);
+ void (*recycle)(struct amvdec_core *core, u32 buf_idx);
+ void (*drain)(struct amvdec_session *sess);
+ void (*resume)(struct amvdec_session *sess);
+ const u8 * (*eos_sequence)(u32 *len);
+ irqreturn_t (*isr)(struct amvdec_session *sess);
+ irqreturn_t (*threaded_isr)(struct amvdec_session *sess);
+};
+
+/**
+ * struct amvdec_format - describes one of the OUTPUT (src) format supported
+ *
+ * @pixfmt: V4L2 pixel format
+ * @min_buffers: minimum amount of CAPTURE (dst) buffers
+ * @max_buffers: maximum amount of CAPTURE (dst) buffers
+ * @max_width: maximum picture width supported
+ * @max_height: maximum picture height supported
+ * @flags: enum flags associated with this pixfmt
+ * @vdec_ops: the VDEC operations that support this format
+ * @codec_ops: the codec operations that support this format
+ * @firmware_path: Path to the firmware that supports this format
+ * @pixfmts_cap: list of CAPTURE pixel formats available with pixfmt
+ */
+struct amvdec_format {
+ u32 pixfmt;
+ u32 min_buffers;
+ u32 max_buffers;
+ u32 max_width;
+ u32 max_height;
+ u32 flags;
+
+ struct amvdec_ops *vdec_ops;
+ struct amvdec_codec_ops *codec_ops;
+
+ char *firmware_path;
+ u32 pixfmts_cap[4];
+};
+
+enum amvdec_status {
+ STATUS_STOPPED,
+ STATUS_INIT,
+ STATUS_RUNNING,
+ STATUS_NEEDS_RESUME,
+};
+
+/**
+ * struct amvdec_session - decoding session parameters
+ *
+ * @core: reference to the vdec core struct
+ * @fh: v4l2 file handle
+ * @m2m_ctx: v4l2 m2m context
+ * @ctrl_handler: V4L2 control handler
+ * @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE
+ * @lock: cap & out queues lock
+ * @fmt_out: vdec pixel format for the OUTPUT queue
+ * @pixfmt_cap: V4L2 pixel format for the CAPTURE queue
+ * @src_buffer_size: size in bytes of the OUTPUT buffers' only plane
+ * @width: current picture width
+ * @height: current picture height
+ * @colorspace: current colorspace
+ * @ycbcr_enc: current ycbcr_enc
+ * @quantization: current quantization
+ * @xfer_func: current transfer function
+ * @pixelaspect: Pixel Aspect Ratio reported by the decoder
+ * @esparser_queued_bufs: number of buffers currently queued into ESPARSER
+ * @esparser_queue_work: work struct for the ESPARSER to process src buffers
+ * @streamon_cap: stream on flag for capture queue
+ * @streamon_out: stream on flag for output queue
+ * @sequence_cap: capture sequence counter
+ * @sequence_out: output sequence counter
+ * @should_stop: flag set if userspace signaled EOS via command
+ * or empty buffer
+ * @keyframe_found: flag set once a keyframe has been parsed
+ * @num_dst_bufs: number of destination buffers
+ * @changed_format: the format changed
+ * @canvas_alloc: array of all the canvas IDs allocated
+ * @canvas_num: number of canvas IDs allocated
+ * @vififo_vaddr: virtual address for the VIFIFO
+ * @vififo_paddr: physical address for the VIFIFO
+ * @vififo_size: size of the VIFIFO dma alloc
+ * @bufs_recycle: list of buffers that need to be recycled
+ * @bufs_recycle_lock: lock for the bufs_recycle list
+ * @recycle_thread: task struct for the recycling thread
+ * @timestamps: chronological list of src timestamps
+ * @ts_spinlock: spinlock for the timestamps list
+ * @last_irq_jiffies: tracks last time the vdec triggered an IRQ
+ * @last_offset: tracks last offset of vififo
+ * @wrap_count: number of times the vififo wrapped around
+ * @fw_idx_to_vb2_idx: firmware buffer index to vb2 buffer index
+ * @status: current decoding status
+ * @priv: codec private data
+ */
+struct amvdec_session {
+ struct amvdec_core *core;
+
+ struct v4l2_fh fh;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *ctrl_min_buf_capture;
+ struct mutex lock;
+
+ const struct amvdec_format *fmt_out;
+ u32 pixfmt_cap;
+ u32 src_buffer_size;
+
+ u32 width;
+ u32 height;
+ u32 colorspace;
+ u8 ycbcr_enc;
+ u8 quantization;
+ u8 xfer_func;
+
+ struct v4l2_fract pixelaspect;
+
+ atomic_t esparser_queued_bufs;
+ struct work_struct esparser_queue_work;
+
+ unsigned int streamon_cap, streamon_out;
+ unsigned int sequence_cap, sequence_out;
+ unsigned int should_stop;
+ unsigned int keyframe_found;
+ unsigned int num_dst_bufs;
+ unsigned int changed_format;
+
+ u8 canvas_alloc[MAX_CANVAS];
+ u32 canvas_num;
+
+ void *vififo_vaddr;
+ dma_addr_t vififo_paddr;
+ u32 vififo_size;
+
+ struct list_head bufs_recycle;
+ struct mutex bufs_recycle_lock; /* bufs_recycle list lock */
+ struct task_struct *recycle_thread;
+
+ struct list_head timestamps;
+ spinlock_t ts_spinlock; /* timestamp list lock */
+
+ u64 last_irq_jiffies;
+ u32 last_offset;
+ u32 wrap_count;
+ u32 fw_idx_to_vb2_idx[32];
+
+ enum amvdec_status status;
+ void *priv;
+};
+
+static inline struct amvdec_session *file_to_amvdec_session(struct file *filp)
+{
+ return container_of(file_to_v4l2_fh(filp), struct amvdec_session, fh);
+}
+
+u32 amvdec_get_output_size(struct amvdec_session *sess);
+
+#endif
--
2.54.0.windows.1
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
2026-07-13 18:16 ` Alfie Varghese
(?)
@ 2026-07-13 18:33 ` sashiko-bot
-1 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-13 18:33 UTC (permalink / raw)
To: Alfie Varghese; +Cc: neil.armstrong, linux-amlogic
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [High] Use-After-Free in vdec_close() due to cancel_work_sync() ordered after v4l2_m2m_ctx_release().
- [High] Structural Use-After-Free of core->m2m_dev on driver unbind due to improper lifecycle management.
- [Medium] Memory leak of sess->priv on the kthread_run() error path in vdec_start_streaming().
- [Low] Compilation error in vdec_m2m_job_abort() due to removed sess->m2m_dev.
Pre-existing issues:
- [High] TOCTOU race in vdec_stop_streaming() places synchronize_irq() after freeing session data, risking UAF and hardware crashes.
--
--- Patch [1]: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs ---
commit 3fdf8cd968dfefcd7c68b91efa837b4576ff3069
Author: Alfie Varghese <alfievarghese22@gmail.com>
staging: media: meson: vdec: fix advanced concurrency and UAF bugs
This commit fixes several deep concurrency and race condition issues identified
in the Amlogic video decoder driver.
> 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
[ ... ]
> @@ -337,9 +337,16 @@ 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 = kthread_run(vdec_recycle_thread, sess,
> "vdec_recycle");
> + if (IS_ERR(sess->recycle_thread)) {
> + ret = PTR_ERR(sess->recycle_thread);
> + sess->recycle_thread = NULL;
> + vdec_poweroff(sess);
> + goto vififo_free;
[Severity: Medium]
Does jumping to vififo_free here leak sess->priv?
Because sess->status is not set to STATUS_INIT until after this block,
it remains STATUS_STOPPED. The subsequent cleanup path in
vdec_stop_streaming() evaluates the status condition as false and skips
its teardown block entirely, leaving sess->priv allocated.
[ ... ]
> @@ -416,6 +423,7 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> kfree(sess->priv);
> sess->priv = NULL;
> core->cur_sess = NULL;
> + synchronize_irq(core->irq);
[Severity: High]
This isn't a bug introduced by this patch, but does the new synchronize_irq()
call actually close the TOCTOU race window?
The commit message states it fixes a TOCTOU race by ensuring running
interrupt handlers finish before freeing session data. However, if an
interrupt fires just before vdec_poweroff(sess) is called in this function,
the ISR reads core->cur_sess locklessly and evaluates it as valid.
The thread executing vdec_stop_streaming() then continues, disables clocks
in vdec_poweroff(), and frees sess->priv. Since synchronize_irq() is placed
at the very end of the block, the concurrent ISR will access unclocked
registers or dereference the freed sess->priv.
[ ... ]
> @@ -927,10 +926,12 @@ 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);
> + cancel_work_sync(&sess->esparser_queue_work);
[Severity: High]
If the esparser_queue_work is concurrently executing or scheduled when
vdec_close() is called, will this cause a use-after-free?
The M2M context is first released and freed by v4l2_m2m_ctx_release().
Immediately after, if the work function esparser_queue_all_src() runs,
it iterates using v4l2_m2m_for_each_src_buf_safe(sess->m2m_ctx, ...),
which dereferences the already-freed memory. Should cancel_work_sync()
be called before v4l2_m2m_ctx_release()?
[ ... ]
> @@ -1105,6 +1126,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]
Is it safe to unconditionally free core->m2m_dev here during driver unbind?
If a userspace process holds the /dev/videoX file descriptor open during
driver unbind, video_unregister_device() will return but the file remains
open.
When the user later closes the file descriptor, vdec_close() invokes
v4l2_m2m_ctx_release(sess->m2m_ctx), which internally accesses the now-freed
m2m_ctx->m2m_dev to acquire its job spinlock.
> diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
> --- a/drivers/staging/media/meson/vdec/vdec.h
> +++ b/drivers/staging/media/meson/vdec/vdec.h
[ ... ]
> @@ -230,7 +233,6 @@ struct amvdec_session {
> struct amvdec_core *core;
>
> struct v4l2_fh fh;
> - struct v4l2_m2m_dev *m2m_dev;
[Severity: Low]
Does removing this field cause a compilation error in vdec_m2m_job_abort()?
It looks like vdec_m2m_job_abort() in vdec.c was not updated and might
still reference sess->m2m_dev, which would break the build.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713181602.773-1-alfievarghese22@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] 11+ messages in thread
* Re: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
2026-07-13 18:16 ` Alfie Varghese
@ 2026-07-13 19:37 ` Ferass El Hafidi
-1 siblings, 0 replies; 11+ messages in thread
From: Ferass El Hafidi @ 2026-07-13 19:37 UTC (permalink / raw)
To: linux-amlogic, Alfie Varghese, neil.armstrong, gregkh,
linux-media, linux-staging, linux-kernel
Cc: Alfie Varghese
On Mon, 13 Jul 2026 18:16, Alfie Varghese <alfievarghese22@gmail.com> wrote:
>This commit fixes several deep concurrency and race condition issues identified
>in the Amlogic video decoder driver:
>
>1. Fix a Use-After-Free window in vdec_close() by moving
> cancel_work_sync(&sess->esparser_queue_work) to execute after the hardware
> is stopped via v4l2_m2m_ctx_release(), preventing interrupts from re-arming it.
>2. Fix a Use-After-Free relating to v4l2_m2m_release(). The driver incorrectly
> allocated the m2m_dev per-session rather than per-device. Moved the
> m2m_dev allocation to vdec_probe() and cleanup to vdec_remove() to fix
> the structural UAF when background m2m jobs were pending.
>3. Fix a TOCTOU race condition in vdec_isr() by adding synchronize_irq()
> in vdec_stop_streaming() after clearing core->cur_sess, ensuring that
> running interrupt handlers finish before freeing session data.
>4. Fix a potential NULL/ERR pointer dereference in vdec_start_streaming()
> by checking the return value of kthread_run() and handling errors gracefully.
>5. Fix a memory leak in vdec_close() by freeing the session control
> handler via v4l2_ctrl_handler_free().
>6. Prevent a potential NULL pointer dereference in vdec_probe() by adding
> a NULL check for of_match_node().
>
>Signed-off-by: Alfie Varghese <alfievarghese22@gmail.com>
>---
> drivers/staging/media/meson/vdec/vdec.c | 52 ++-
> drivers/staging/media/meson/vdec/vdec.h | 586 ++++++++++++------------
> 2 files changed, 331 insertions(+), 307 deletions(-)
>
>diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
>index a039d925c0fe..f57c473da5e1 100644
>--- a/drivers/staging/media/meson/vdec/vdec.c
>+++ b/drivers/staging/media/meson/vdec/vdec.c
>@@ -337,9 +337,16 @@ 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 = kthread_run(vdec_recycle_thread, sess,
> "vdec_recycle");
>+ if (IS_ERR(sess->recycle_thread)) {
>+ ret = PTR_ERR(sess->recycle_thread);
>+ sess->recycle_thread = NULL;
>+ vdec_poweroff(sess);
>+ goto vififo_free;
>+ }
>+ }
>
> sess->status = STATUS_INIT;
> core->cur_sess = sess;
>@@ -404,7 +411,7 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> sess->status == STATUS_INIT ||
> (sess->status == STATUS_NEEDS_RESUME &&
> (!sess->streamon_out || !sess->streamon_cap))) {
>- if (vdec_codec_needs_recycle(sess))
>+ if (vdec_codec_needs_recycle(sess) && sess->recycle_thread)
> kthread_stop(sess->recycle_thread);
>
> vdec_poweroff(sess);
>@@ -416,6 +423,7 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> kfree(sess->priv);
> sess->priv = NULL;
> core->cur_sess = NULL;
>+ synchronize_irq(core->irq);
> sess->status = STATUS_STOPPED;
> }
>
>@@ -873,18 +881,11 @@ 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);
>@@ -915,8 +916,6 @@ static int vdec_open(struct file *file)
>
> err_m2m_ctx_release:
> v4l2_m2m_ctx_release(sess->m2m_ctx);
>-err_m2m_release:
>- v4l2_m2m_release(sess->m2m_dev);
> err_free_sess:
> kfree(sess);
> return ret;
>@@ -927,10 +926,12 @@ 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);
>+ cancel_work_sync(&sess->esparser_queue_work);
> 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);
>
>@@ -953,6 +954,9 @@ static irqreturn_t vdec_isr(int irq, void *data)
> struct amvdec_core *core = data;
> struct amvdec_session *sess = core->cur_sess;
>
>+ if (!sess)
>+ return IRQ_NONE;
>+
> sess->last_irq_jiffies = get_jiffies_64();
>
> return sess->fmt_out->codec_ops->isr(sess);
>@@ -963,6 +967,9 @@ static irqreturn_t vdec_threaded_isr(int irq, void *data)
> struct amvdec_core *core = data;
> struct amvdec_session *sess = core->cur_sess;
>
>+ if (!sess)
>+ return IRQ_NONE;
>+
> return sess->fmt_out->codec_ops->threaded_isr(sess);
> }
>
>@@ -1020,6 +1027,8 @@ static int vdec_probe(struct platform_device *pdev)
> return PTR_ERR(core->canvas);
>
> of_id = of_match_node(vdec_dt_match, dev->of_node);
>+ if (!of_id)
>+ return -ENODEV;
> core->platform = of_id->data;
>
> if (core->platform->revision == VDEC_REVISION_G12A ||
>@@ -1049,6 +1058,8 @@ static int vdec_probe(struct platform_device *pdev)
> if (irq < 0)
> return irq;
>
>+ core->irq = irq;
>+
> ret = devm_request_threaded_irq(core->dev, irq, vdec_isr,
> vdec_threaded_isr, IRQF_ONESHOT,
> "vdec", core);
>@@ -1065,10 +1076,17 @@ static int vdec_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
>+ core->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
>+ if (IS_ERR(core->m2m_dev)) {
>+ dev_err(dev, "Failed to init v4l2 m2m dev\n");
>+ ret = PTR_ERR(core->m2m_dev);
>+ goto err_v4l2_unreg;
>+ }
>+
> vdev = video_device_alloc();
> if (!vdev) {
> ret = -ENOMEM;
>- goto err_vdev_release;
>+ goto err_m2m_release;
> }
>
> core->vdev_dec = vdev;
>@@ -1096,6 +1114,9 @@ static int vdec_probe(struct platform_device *pdev)
>
> err_vdev_release:
> video_device_release(vdev);
>+err_m2m_release:
>+ v4l2_m2m_release(core->m2m_dev);
>+err_v4l2_unreg:
> v4l2_device_unregister(&core->v4l2_dev);
> return ret;
> }
>@@ -1105,6 +1126,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);
> }
>
It probably would be better to split into multiple commits to make
it easier for maintainers to review your patch.
>diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
>index 7a5d8e871d70..b2a606e965b4 100644
>--- a/drivers/staging/media/meson/vdec/vdec.h
>+++ b/drivers/staging/media/meson/vdec/vdec.h
>@@ -1,292 +1,294 @@
>-/* SPDX-License-Identifier: GPL-2.0+ */
>-/*
>- * Copyright (C) 2018 BayLibre, SAS
>- * Author: Maxime Jourdan <mjourdan@baylibre.com>
>- */
>-
>-#ifndef __MESON_VDEC_CORE_H_
>-#define __MESON_VDEC_CORE_H_
>-
>-#include <linux/irqreturn.h>
>-#include <linux/regmap.h>
>-#include <linux/list.h>
>-#include <media/videobuf2-v4l2.h>
>-#include <media/v4l2-ctrls.h>
>-#include <media/v4l2-device.h>
>-#include <linux/soc/amlogic/meson-canvas.h>
>-
>-#include "vdec_platform.h"
>-
>-/* 32 buffers in 3-plane YUV420 */
>-#define MAX_CANVAS (32 * 3)
>-
>-struct amvdec_buffer {
>- struct list_head list;
>- struct vb2_buffer *vb;
>-};
>-
>-/**
>- * struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset
>- *
>- * @list: used to make lists out of this struct
>- * @tc: timecode from the v4l2 buffer
>- * @ts: timestamp from the VB2 buffer
>- * @offset: offset in the VIFIFO where the associated packet was written
>- * @flags: flags from the v4l2 buffer
>- * @used_count: times this timestamp was checked for a match with a dst buffer
>- */
>-struct amvdec_timestamp {
>- struct list_head list;
>- struct v4l2_timecode tc;
>- u64 ts;
>- u32 offset;
>- u32 flags;
>- u32 used_count;
>-};
>-
>-struct amvdec_session;
>-
>-/**
>- * struct amvdec_core - device parameters, singleton
>- *
>- * @dos_base: DOS memory base address
>- * @esparser_base: PARSER memory base address
>- * @regmap_ao: regmap for the AO bus
>- * @dev: core device
>- * @dev_dec: decoder device
>- * @platform: platform-specific data
>- * @canvas: canvas provider reference
>- * @dos_parser_clk: DOS_PARSER clock
>- * @dos_clk: DOS clock
>- * @vdec_1_clk: VDEC_1 clock
>- * @vdec_hevc_clk: VDEC_HEVC clock
>- * @vdec_hevcf_clk: VDEC_HEVCF clock
>- * @esparser_reset: RESET for the PARSER
>- * @vdev_dec: video device for the decoder
>- * @v4l2_dev: v4l2 device
>- * @cur_sess: current decoding session
>- * @lock: video device lock
>- */
>-struct amvdec_core {
>- void __iomem *dos_base;
>- void __iomem *esparser_base;
>- struct regmap *regmap_ao;
>-
>- struct device *dev;
>- struct device *dev_dec;
>- const struct vdec_platform *platform;
>-
>- struct meson_canvas *canvas;
>-
>- struct clk *dos_parser_clk;
>- struct clk *dos_clk;
>- struct clk *vdec_1_clk;
>- struct clk *vdec_hevc_clk;
>- struct clk *vdec_hevcf_clk;
>-
>- struct reset_control *esparser_reset;
>-
>- struct video_device *vdev_dec;
>- struct v4l2_device v4l2_dev;
>-
>- struct amvdec_session *cur_sess;
>- struct mutex lock;
>-};
>-
>-/**
>- * struct amvdec_ops - vdec operations
>- *
>- * @start: mandatory call when the vdec needs to initialize
>- * @stop: mandatory call when the vdec needs to stop
>- * @conf_esparser: mandatory call to let the vdec configure the ESPARSER
>- * @vififo_level: mandatory call to get the current amount of data
>- * in the VIFIFO
>- */
>-struct amvdec_ops {
>- int (*start)(struct amvdec_session *sess);
>- int (*stop)(struct amvdec_session *sess);
>- void (*conf_esparser)(struct amvdec_session *sess);
>- u32 (*vififo_level)(struct amvdec_session *sess);
>-};
>-
>-/**
>- * struct amvdec_codec_ops - codec operations
>- *
>- * @start: mandatory call when the codec needs to initialize
>- * @stop: mandatory call when the codec needs to stop
>- * @load_extended_firmware: optional call to load additional firmware bits
>- * @num_pending_bufs: optional call to get the number of dst buffers on hold
>- * @can_recycle: optional call to know if the codec is ready to recycle
>- * a dst buffer
>- * @recycle: optional call to tell the codec to recycle a dst buffer. Must go
>- * in pair with @can_recycle
>- * @drain: optional call if the codec has a custom way of draining
>- * @resume: optional call to resume after a resolution change
>- * @eos_sequence: optional call to get an end sequence to send to esparser
>- * for flush. Mutually exclusive with @drain.
>- * @isr: mandatory call when the ISR triggers
>- * @threaded_isr: mandatory call for the threaded ISR
>- */
>-struct amvdec_codec_ops {
>- int (*start)(struct amvdec_session *sess);
>- int (*stop)(struct amvdec_session *sess);
>- int (*load_extended_firmware)(struct amvdec_session *sess,
>- const u8 *data, u32 len);
>- u32 (*num_pending_bufs)(struct amvdec_session *sess);
>- int (*can_recycle)(struct amvdec_core *core);
>- void (*recycle)(struct amvdec_core *core, u32 buf_idx);
>- void (*drain)(struct amvdec_session *sess);
>- void (*resume)(struct amvdec_session *sess);
>- const u8 * (*eos_sequence)(u32 *len);
>- irqreturn_t (*isr)(struct amvdec_session *sess);
>- irqreturn_t (*threaded_isr)(struct amvdec_session *sess);
>-};
>-
>-/**
>- * struct amvdec_format - describes one of the OUTPUT (src) format supported
>- *
>- * @pixfmt: V4L2 pixel format
>- * @min_buffers: minimum amount of CAPTURE (dst) buffers
>- * @max_buffers: maximum amount of CAPTURE (dst) buffers
>- * @max_width: maximum picture width supported
>- * @max_height: maximum picture height supported
>- * @flags: enum flags associated with this pixfmt
>- * @vdec_ops: the VDEC operations that support this format
>- * @codec_ops: the codec operations that support this format
>- * @firmware_path: Path to the firmware that supports this format
>- * @pixfmts_cap: list of CAPTURE pixel formats available with pixfmt
>- */
>-struct amvdec_format {
>- u32 pixfmt;
>- u32 min_buffers;
>- u32 max_buffers;
>- u32 max_width;
>- u32 max_height;
>- u32 flags;
>-
>- struct amvdec_ops *vdec_ops;
>- struct amvdec_codec_ops *codec_ops;
>-
>- char *firmware_path;
>- u32 pixfmts_cap[4];
>-};
>-
>-enum amvdec_status {
>- STATUS_STOPPED,
>- STATUS_INIT,
>- STATUS_RUNNING,
>- STATUS_NEEDS_RESUME,
>-};
>-
>-/**
>- * struct amvdec_session - decoding session parameters
>- *
>- * @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
>- * @lock: cap & out queues lock
>- * @fmt_out: vdec pixel format for the OUTPUT queue
>- * @pixfmt_cap: V4L2 pixel format for the CAPTURE queue
>- * @src_buffer_size: size in bytes of the OUTPUT buffers' only plane
>- * @width: current picture width
>- * @height: current picture height
>- * @colorspace: current colorspace
>- * @ycbcr_enc: current ycbcr_enc
>- * @quantization: current quantization
>- * @xfer_func: current transfer function
>- * @pixelaspect: Pixel Aspect Ratio reported by the decoder
>- * @esparser_queued_bufs: number of buffers currently queued into ESPARSER
>- * @esparser_queue_work: work struct for the ESPARSER to process src buffers
>- * @streamon_cap: stream on flag for capture queue
>- * @streamon_out: stream on flag for output queue
>- * @sequence_cap: capture sequence counter
>- * @sequence_out: output sequence counter
>- * @should_stop: flag set if userspace signaled EOS via command
>- * or empty buffer
>- * @keyframe_found: flag set once a keyframe has been parsed
>- * @num_dst_bufs: number of destination buffers
>- * @changed_format: the format changed
>- * @canvas_alloc: array of all the canvas IDs allocated
>- * @canvas_num: number of canvas IDs allocated
>- * @vififo_vaddr: virtual address for the VIFIFO
>- * @vififo_paddr: physical address for the VIFIFO
>- * @vififo_size: size of the VIFIFO dma alloc
>- * @bufs_recycle: list of buffers that need to be recycled
>- * @bufs_recycle_lock: lock for the bufs_recycle list
>- * @recycle_thread: task struct for the recycling thread
>- * @timestamps: chronological list of src timestamps
>- * @ts_spinlock: spinlock for the timestamps list
>- * @last_irq_jiffies: tracks last time the vdec triggered an IRQ
>- * @last_offset: tracks last offset of vififo
>- * @wrap_count: number of times the vififo wrapped around
>- * @fw_idx_to_vb2_idx: firmware buffer index to vb2 buffer index
>- * @status: current decoding status
>- * @priv: codec private data
>- */
>-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;
>- struct mutex lock;
>-
>- const struct amvdec_format *fmt_out;
>- u32 pixfmt_cap;
>- u32 src_buffer_size;
>-
>- u32 width;
>- u32 height;
>- u32 colorspace;
>- u8 ycbcr_enc;
>- u8 quantization;
>- u8 xfer_func;
>-
>- struct v4l2_fract pixelaspect;
>-
>- atomic_t esparser_queued_bufs;
>- struct work_struct esparser_queue_work;
>-
>- unsigned int streamon_cap, streamon_out;
>- unsigned int sequence_cap, sequence_out;
>- unsigned int should_stop;
>- unsigned int keyframe_found;
>- unsigned int num_dst_bufs;
>- unsigned int changed_format;
>-
>- u8 canvas_alloc[MAX_CANVAS];
>- u32 canvas_num;
>-
>- void *vififo_vaddr;
>- dma_addr_t vififo_paddr;
>- u32 vififo_size;
>-
>- struct list_head bufs_recycle;
>- struct mutex bufs_recycle_lock; /* bufs_recycle list lock */
>- struct task_struct *recycle_thread;
>-
>- struct list_head timestamps;
>- spinlock_t ts_spinlock; /* timestamp list lock */
>-
>- u64 last_irq_jiffies;
>- u32 last_offset;
>- u32 wrap_count;
>- u32 fw_idx_to_vb2_idx[32];
>-
>- enum amvdec_status status;
>- void *priv;
>-};
>-
>-static inline struct amvdec_session *file_to_amvdec_session(struct file *filp)
>-{
>- return container_of(file_to_v4l2_fh(filp), struct amvdec_session, fh);
>-}
>-
>-u32 amvdec_get_output_size(struct amvdec_session *sess);
>-
>-#endif
>+/* SPDX-License-Identifier: GPL-2.0+ */
>+/*
>+ * Copyright (C) 2018 BayLibre, SAS
>+ * Author: Maxime Jourdan <mjourdan@baylibre.com>
>+ */
>+
>+#ifndef __MESON_VDEC_CORE_H_
>+#define __MESON_VDEC_CORE_H_
>+
>+#include <linux/irqreturn.h>
>+#include <linux/regmap.h>
>+#include <linux/list.h>
>+#include <media/videobuf2-v4l2.h>
>+#include <media/v4l2-ctrls.h>
>+#include <media/v4l2-device.h>
>+#include <linux/soc/amlogic/meson-canvas.h>
>+
>+#include "vdec_platform.h"
>+
>+/* 32 buffers in 3-plane YUV420 */
>+#define MAX_CANVAS (32 * 3)
>+
>+struct amvdec_buffer {
>+ struct list_head list;
>+ struct vb2_buffer *vb;
>+};
>+
>+/**
>+ * struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset
>+ *
>+ * @list: used to make lists out of this struct
>+ * @tc: timecode from the v4l2 buffer
>+ * @ts: timestamp from the VB2 buffer
>+ * @offset: offset in the VIFIFO where the associated packet was written
>+ * @flags: flags from the v4l2 buffer
>+ * @used_count: times this timestamp was checked for a match with a dst buffer
>+ */
>+struct amvdec_timestamp {
>+ struct list_head list;
>+ struct v4l2_timecode tc;
>+ u64 ts;
>+ u32 offset;
>+ u32 flags;
>+ u32 used_count;
>+};
>+
>+struct amvdec_session;
>+
>+/**
>+ * struct amvdec_core - device parameters, singleton
>+ *
>+ * @dos_base: DOS memory base address
>+ * @esparser_base: PARSER memory base address
>+ * @regmap_ao: regmap for the AO bus
>+ * @dev: core device
>+ * @dev_dec: decoder device
>+ * @platform: platform-specific data
>+ * @canvas: canvas provider reference
>+ * @dos_parser_clk: DOS_PARSER clock
>+ * @dos_clk: DOS clock
>+ * @vdec_1_clk: VDEC_1 clock
>+ * @vdec_hevc_clk: VDEC_HEVC clock
>+ * @vdec_hevcf_clk: VDEC_HEVCF clock
>+ * @esparser_reset: RESET for the PARSER
>+ * @vdev_dec: video device for the decoder
>+ * @v4l2_dev: v4l2 device
>+ * @m2m_dev: v4l2 m2m device
>+ * @cur_sess: current decoding session
>+ * @lock: video device lock
>+ * @irq: decoder irq
>+ */
>+struct amvdec_core {
>+ void __iomem *dos_base;
>+ void __iomem *esparser_base;
>+ struct regmap *regmap_ao;
>+
>+ struct device *dev;
>+ struct device *dev_dec;
>+ const struct vdec_platform *platform;
>+
>+ struct meson_canvas *canvas;
>+
>+ struct clk *dos_parser_clk;
>+ struct clk *dos_clk;
>+ struct clk *vdec_1_clk;
>+ struct clk *vdec_hevc_clk;
>+ struct clk *vdec_hevcf_clk;
>+
>+ struct reset_control *esparser_reset;
>+
>+ struct video_device *vdev_dec;
>+ struct v4l2_device v4l2_dev;
>+ struct v4l2_m2m_dev *m2m_dev;
>+
>+ struct amvdec_session *cur_sess;
>+ struct mutex lock;
>+ int irq;
>+};
>+
>+/**
>+ * struct amvdec_ops - vdec operations
>+ *
>+ * @start: mandatory call when the vdec needs to initialize
>+ * @stop: mandatory call when the vdec needs to stop
>+ * @conf_esparser: mandatory call to let the vdec configure the ESPARSER
>+ * @vififo_level: mandatory call to get the current amount of data
>+ * in the VIFIFO
>+ */
>+struct amvdec_ops {
>+ int (*start)(struct amvdec_session *sess);
>+ int (*stop)(struct amvdec_session *sess);
>+ void (*conf_esparser)(struct amvdec_session *sess);
>+ u32 (*vififo_level)(struct amvdec_session *sess);
>+};
>+
>+/**
>+ * struct amvdec_codec_ops - codec operations
>+ *
>+ * @start: mandatory call when the codec needs to initialize
>+ * @stop: mandatory call when the codec needs to stop
>+ * @load_extended_firmware: optional call to load additional firmware bits
>+ * @num_pending_bufs: optional call to get the number of dst buffers on hold
>+ * @can_recycle: optional call to know if the codec is ready to recycle
>+ * a dst buffer
>+ * @recycle: optional call to tell the codec to recycle a dst buffer. Must go
>+ * in pair with @can_recycle
>+ * @drain: optional call if the codec has a custom way of draining
>+ * @resume: optional call to resume after a resolution change
>+ * @eos_sequence: optional call to get an end sequence to send to esparser
>+ * for flush. Mutually exclusive with @drain.
>+ * @isr: mandatory call when the ISR triggers
>+ * @threaded_isr: mandatory call for the threaded ISR
>+ */
>+struct amvdec_codec_ops {
>+ int (*start)(struct amvdec_session *sess);
>+ int (*stop)(struct amvdec_session *sess);
>+ int (*load_extended_firmware)(struct amvdec_session *sess,
>+ const u8 *data, u32 len);
>+ u32 (*num_pending_bufs)(struct amvdec_session *sess);
>+ int (*can_recycle)(struct amvdec_core *core);
>+ void (*recycle)(struct amvdec_core *core, u32 buf_idx);
>+ void (*drain)(struct amvdec_session *sess);
>+ void (*resume)(struct amvdec_session *sess);
>+ const u8 * (*eos_sequence)(u32 *len);
>+ irqreturn_t (*isr)(struct amvdec_session *sess);
>+ irqreturn_t (*threaded_isr)(struct amvdec_session *sess);
>+};
>+
>+/**
>+ * struct amvdec_format - describes one of the OUTPUT (src) format supported
>+ *
>+ * @pixfmt: V4L2 pixel format
>+ * @min_buffers: minimum amount of CAPTURE (dst) buffers
>+ * @max_buffers: maximum amount of CAPTURE (dst) buffers
>+ * @max_width: maximum picture width supported
>+ * @max_height: maximum picture height supported
>+ * @flags: enum flags associated with this pixfmt
>+ * @vdec_ops: the VDEC operations that support this format
>+ * @codec_ops: the codec operations that support this format
>+ * @firmware_path: Path to the firmware that supports this format
>+ * @pixfmts_cap: list of CAPTURE pixel formats available with pixfmt
>+ */
>+struct amvdec_format {
>+ u32 pixfmt;
>+ u32 min_buffers;
>+ u32 max_buffers;
>+ u32 max_width;
>+ u32 max_height;
>+ u32 flags;
>+
>+ struct amvdec_ops *vdec_ops;
>+ struct amvdec_codec_ops *codec_ops;
>+
>+ char *firmware_path;
>+ u32 pixfmts_cap[4];
>+};
>+
>+enum amvdec_status {
>+ STATUS_STOPPED,
>+ STATUS_INIT,
>+ STATUS_RUNNING,
>+ STATUS_NEEDS_RESUME,
>+};
>+
>+/**
>+ * struct amvdec_session - decoding session parameters
>+ *
>+ * @core: reference to the vdec core struct
>+ * @fh: v4l2 file handle
>+ * @m2m_ctx: v4l2 m2m context
>+ * @ctrl_handler: V4L2 control handler
>+ * @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE
>+ * @lock: cap & out queues lock
>+ * @fmt_out: vdec pixel format for the OUTPUT queue
>+ * @pixfmt_cap: V4L2 pixel format for the CAPTURE queue
>+ * @src_buffer_size: size in bytes of the OUTPUT buffers' only plane
>+ * @width: current picture width
>+ * @height: current picture height
>+ * @colorspace: current colorspace
>+ * @ycbcr_enc: current ycbcr_enc
>+ * @quantization: current quantization
>+ * @xfer_func: current transfer function
>+ * @pixelaspect: Pixel Aspect Ratio reported by the decoder
>+ * @esparser_queued_bufs: number of buffers currently queued into ESPARSER
>+ * @esparser_queue_work: work struct for the ESPARSER to process src buffers
>+ * @streamon_cap: stream on flag for capture queue
>+ * @streamon_out: stream on flag for output queue
>+ * @sequence_cap: capture sequence counter
>+ * @sequence_out: output sequence counter
>+ * @should_stop: flag set if userspace signaled EOS via command
>+ * or empty buffer
>+ * @keyframe_found: flag set once a keyframe has been parsed
>+ * @num_dst_bufs: number of destination buffers
>+ * @changed_format: the format changed
>+ * @canvas_alloc: array of all the canvas IDs allocated
>+ * @canvas_num: number of canvas IDs allocated
>+ * @vififo_vaddr: virtual address for the VIFIFO
>+ * @vififo_paddr: physical address for the VIFIFO
>+ * @vififo_size: size of the VIFIFO dma alloc
>+ * @bufs_recycle: list of buffers that need to be recycled
>+ * @bufs_recycle_lock: lock for the bufs_recycle list
>+ * @recycle_thread: task struct for the recycling thread
>+ * @timestamps: chronological list of src timestamps
>+ * @ts_spinlock: spinlock for the timestamps list
>+ * @last_irq_jiffies: tracks last time the vdec triggered an IRQ
>+ * @last_offset: tracks last offset of vififo
>+ * @wrap_count: number of times the vififo wrapped around
>+ * @fw_idx_to_vb2_idx: firmware buffer index to vb2 buffer index
>+ * @status: current decoding status
>+ * @priv: codec private data
>+ */
>+struct amvdec_session {
>+ struct amvdec_core *core;
>+
>+ struct v4l2_fh fh;
>+ struct v4l2_m2m_ctx *m2m_ctx;
>+ struct v4l2_ctrl_handler ctrl_handler;
>+ struct v4l2_ctrl *ctrl_min_buf_capture;
>+ struct mutex lock;
>+
>+ const struct amvdec_format *fmt_out;
>+ u32 pixfmt_cap;
>+ u32 src_buffer_size;
>+
>+ u32 width;
>+ u32 height;
>+ u32 colorspace;
>+ u8 ycbcr_enc;
>+ u8 quantization;
>+ u8 xfer_func;
>+
>+ struct v4l2_fract pixelaspect;
>+
>+ atomic_t esparser_queued_bufs;
>+ struct work_struct esparser_queue_work;
>+
>+ unsigned int streamon_cap, streamon_out;
>+ unsigned int sequence_cap, sequence_out;
>+ unsigned int should_stop;
>+ unsigned int keyframe_found;
>+ unsigned int num_dst_bufs;
>+ unsigned int changed_format;
>+
>+ u8 canvas_alloc[MAX_CANVAS];
>+ u32 canvas_num;
>+
>+ void *vififo_vaddr;
>+ dma_addr_t vififo_paddr;
>+ u32 vififo_size;
>+
>+ struct list_head bufs_recycle;
>+ struct mutex bufs_recycle_lock; /* bufs_recycle list lock */
>+ struct task_struct *recycle_thread;
>+
>+ struct list_head timestamps;
>+ spinlock_t ts_spinlock; /* timestamp list lock */
>+
>+ u64 last_irq_jiffies;
>+ u32 last_offset;
>+ u32 wrap_count;
>+ u32 fw_idx_to_vb2_idx[32];
>+
>+ enum amvdec_status status;
>+ void *priv;
>+};
>+
>+static inline struct amvdec_session *file_to_amvdec_session(struct file *filp)
>+{
>+ return container_of(file_to_v4l2_fh(filp), struct amvdec_session, fh);
>+}
>+
>+u32 amvdec_get_output_size(struct amvdec_session *sess);
>+
>+#endif
What is this huge diff?
>--
>2.54.0.windows.1
>
>
>_______________________________________________
>linux-amlogic mailing list
>linux-amlogic@lists.infradead.org
>http://lists.infradead.org/mailman/listinfo/linux-amlogic
Best regards,
Ferass
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
@ 2026-07-13 19:37 ` Ferass El Hafidi
0 siblings, 0 replies; 11+ messages in thread
From: Ferass El Hafidi @ 2026-07-13 19:37 UTC (permalink / raw)
To: linux-amlogic, Alfie Varghese, neil.armstrong, gregkh,
linux-media, linux-staging, linux-kernel
Cc: Alfie Varghese
On Mon, 13 Jul 2026 18:16, Alfie Varghese <alfievarghese22@gmail.com> wrote:
>This commit fixes several deep concurrency and race condition issues identified
>in the Amlogic video decoder driver:
>
>1. Fix a Use-After-Free window in vdec_close() by moving
> cancel_work_sync(&sess->esparser_queue_work) to execute after the hardware
> is stopped via v4l2_m2m_ctx_release(), preventing interrupts from re-arming it.
>2. Fix a Use-After-Free relating to v4l2_m2m_release(). The driver incorrectly
> allocated the m2m_dev per-session rather than per-device. Moved the
> m2m_dev allocation to vdec_probe() and cleanup to vdec_remove() to fix
> the structural UAF when background m2m jobs were pending.
>3. Fix a TOCTOU race condition in vdec_isr() by adding synchronize_irq()
> in vdec_stop_streaming() after clearing core->cur_sess, ensuring that
> running interrupt handlers finish before freeing session data.
>4. Fix a potential NULL/ERR pointer dereference in vdec_start_streaming()
> by checking the return value of kthread_run() and handling errors gracefully.
>5. Fix a memory leak in vdec_close() by freeing the session control
> handler via v4l2_ctrl_handler_free().
>6. Prevent a potential NULL pointer dereference in vdec_probe() by adding
> a NULL check for of_match_node().
>
>Signed-off-by: Alfie Varghese <alfievarghese22@gmail.com>
>---
> drivers/staging/media/meson/vdec/vdec.c | 52 ++-
> drivers/staging/media/meson/vdec/vdec.h | 586 ++++++++++++------------
> 2 files changed, 331 insertions(+), 307 deletions(-)
>
>diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
>index a039d925c0fe..f57c473da5e1 100644
>--- a/drivers/staging/media/meson/vdec/vdec.c
>+++ b/drivers/staging/media/meson/vdec/vdec.c
>@@ -337,9 +337,16 @@ 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 = kthread_run(vdec_recycle_thread, sess,
> "vdec_recycle");
>+ if (IS_ERR(sess->recycle_thread)) {
>+ ret = PTR_ERR(sess->recycle_thread);
>+ sess->recycle_thread = NULL;
>+ vdec_poweroff(sess);
>+ goto vififo_free;
>+ }
>+ }
>
> sess->status = STATUS_INIT;
> core->cur_sess = sess;
>@@ -404,7 +411,7 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> sess->status == STATUS_INIT ||
> (sess->status == STATUS_NEEDS_RESUME &&
> (!sess->streamon_out || !sess->streamon_cap))) {
>- if (vdec_codec_needs_recycle(sess))
>+ if (vdec_codec_needs_recycle(sess) && sess->recycle_thread)
> kthread_stop(sess->recycle_thread);
>
> vdec_poweroff(sess);
>@@ -416,6 +423,7 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> kfree(sess->priv);
> sess->priv = NULL;
> core->cur_sess = NULL;
>+ synchronize_irq(core->irq);
> sess->status = STATUS_STOPPED;
> }
>
>@@ -873,18 +881,11 @@ 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);
>@@ -915,8 +916,6 @@ static int vdec_open(struct file *file)
>
> err_m2m_ctx_release:
> v4l2_m2m_ctx_release(sess->m2m_ctx);
>-err_m2m_release:
>- v4l2_m2m_release(sess->m2m_dev);
> err_free_sess:
> kfree(sess);
> return ret;
>@@ -927,10 +926,12 @@ 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);
>+ cancel_work_sync(&sess->esparser_queue_work);
> 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);
>
>@@ -953,6 +954,9 @@ static irqreturn_t vdec_isr(int irq, void *data)
> struct amvdec_core *core = data;
> struct amvdec_session *sess = core->cur_sess;
>
>+ if (!sess)
>+ return IRQ_NONE;
>+
> sess->last_irq_jiffies = get_jiffies_64();
>
> return sess->fmt_out->codec_ops->isr(sess);
>@@ -963,6 +967,9 @@ static irqreturn_t vdec_threaded_isr(int irq, void *data)
> struct amvdec_core *core = data;
> struct amvdec_session *sess = core->cur_sess;
>
>+ if (!sess)
>+ return IRQ_NONE;
>+
> return sess->fmt_out->codec_ops->threaded_isr(sess);
> }
>
>@@ -1020,6 +1027,8 @@ static int vdec_probe(struct platform_device *pdev)
> return PTR_ERR(core->canvas);
>
> of_id = of_match_node(vdec_dt_match, dev->of_node);
>+ if (!of_id)
>+ return -ENODEV;
> core->platform = of_id->data;
>
> if (core->platform->revision == VDEC_REVISION_G12A ||
>@@ -1049,6 +1058,8 @@ static int vdec_probe(struct platform_device *pdev)
> if (irq < 0)
> return irq;
>
>+ core->irq = irq;
>+
> ret = devm_request_threaded_irq(core->dev, irq, vdec_isr,
> vdec_threaded_isr, IRQF_ONESHOT,
> "vdec", core);
>@@ -1065,10 +1076,17 @@ static int vdec_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
>+ core->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
>+ if (IS_ERR(core->m2m_dev)) {
>+ dev_err(dev, "Failed to init v4l2 m2m dev\n");
>+ ret = PTR_ERR(core->m2m_dev);
>+ goto err_v4l2_unreg;
>+ }
>+
> vdev = video_device_alloc();
> if (!vdev) {
> ret = -ENOMEM;
>- goto err_vdev_release;
>+ goto err_m2m_release;
> }
>
> core->vdev_dec = vdev;
>@@ -1096,6 +1114,9 @@ static int vdec_probe(struct platform_device *pdev)
>
> err_vdev_release:
> video_device_release(vdev);
>+err_m2m_release:
>+ v4l2_m2m_release(core->m2m_dev);
>+err_v4l2_unreg:
> v4l2_device_unregister(&core->v4l2_dev);
> return ret;
> }
>@@ -1105,6 +1126,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);
> }
>
It probably would be better to split into multiple commits to make
it easier for maintainers to review your patch.
>diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
>index 7a5d8e871d70..b2a606e965b4 100644
>--- a/drivers/staging/media/meson/vdec/vdec.h
>+++ b/drivers/staging/media/meson/vdec/vdec.h
>@@ -1,292 +1,294 @@
>-/* SPDX-License-Identifier: GPL-2.0+ */
>-/*
>- * Copyright (C) 2018 BayLibre, SAS
>- * Author: Maxime Jourdan <mjourdan@baylibre.com>
>- */
>-
>-#ifndef __MESON_VDEC_CORE_H_
>-#define __MESON_VDEC_CORE_H_
>-
>-#include <linux/irqreturn.h>
>-#include <linux/regmap.h>
>-#include <linux/list.h>
>-#include <media/videobuf2-v4l2.h>
>-#include <media/v4l2-ctrls.h>
>-#include <media/v4l2-device.h>
>-#include <linux/soc/amlogic/meson-canvas.h>
>-
>-#include "vdec_platform.h"
>-
>-/* 32 buffers in 3-plane YUV420 */
>-#define MAX_CANVAS (32 * 3)
>-
>-struct amvdec_buffer {
>- struct list_head list;
>- struct vb2_buffer *vb;
>-};
>-
>-/**
>- * struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset
>- *
>- * @list: used to make lists out of this struct
>- * @tc: timecode from the v4l2 buffer
>- * @ts: timestamp from the VB2 buffer
>- * @offset: offset in the VIFIFO where the associated packet was written
>- * @flags: flags from the v4l2 buffer
>- * @used_count: times this timestamp was checked for a match with a dst buffer
>- */
>-struct amvdec_timestamp {
>- struct list_head list;
>- struct v4l2_timecode tc;
>- u64 ts;
>- u32 offset;
>- u32 flags;
>- u32 used_count;
>-};
>-
>-struct amvdec_session;
>-
>-/**
>- * struct amvdec_core - device parameters, singleton
>- *
>- * @dos_base: DOS memory base address
>- * @esparser_base: PARSER memory base address
>- * @regmap_ao: regmap for the AO bus
>- * @dev: core device
>- * @dev_dec: decoder device
>- * @platform: platform-specific data
>- * @canvas: canvas provider reference
>- * @dos_parser_clk: DOS_PARSER clock
>- * @dos_clk: DOS clock
>- * @vdec_1_clk: VDEC_1 clock
>- * @vdec_hevc_clk: VDEC_HEVC clock
>- * @vdec_hevcf_clk: VDEC_HEVCF clock
>- * @esparser_reset: RESET for the PARSER
>- * @vdev_dec: video device for the decoder
>- * @v4l2_dev: v4l2 device
>- * @cur_sess: current decoding session
>- * @lock: video device lock
>- */
>-struct amvdec_core {
>- void __iomem *dos_base;
>- void __iomem *esparser_base;
>- struct regmap *regmap_ao;
>-
>- struct device *dev;
>- struct device *dev_dec;
>- const struct vdec_platform *platform;
>-
>- struct meson_canvas *canvas;
>-
>- struct clk *dos_parser_clk;
>- struct clk *dos_clk;
>- struct clk *vdec_1_clk;
>- struct clk *vdec_hevc_clk;
>- struct clk *vdec_hevcf_clk;
>-
>- struct reset_control *esparser_reset;
>-
>- struct video_device *vdev_dec;
>- struct v4l2_device v4l2_dev;
>-
>- struct amvdec_session *cur_sess;
>- struct mutex lock;
>-};
>-
>-/**
>- * struct amvdec_ops - vdec operations
>- *
>- * @start: mandatory call when the vdec needs to initialize
>- * @stop: mandatory call when the vdec needs to stop
>- * @conf_esparser: mandatory call to let the vdec configure the ESPARSER
>- * @vififo_level: mandatory call to get the current amount of data
>- * in the VIFIFO
>- */
>-struct amvdec_ops {
>- int (*start)(struct amvdec_session *sess);
>- int (*stop)(struct amvdec_session *sess);
>- void (*conf_esparser)(struct amvdec_session *sess);
>- u32 (*vififo_level)(struct amvdec_session *sess);
>-};
>-
>-/**
>- * struct amvdec_codec_ops - codec operations
>- *
>- * @start: mandatory call when the codec needs to initialize
>- * @stop: mandatory call when the codec needs to stop
>- * @load_extended_firmware: optional call to load additional firmware bits
>- * @num_pending_bufs: optional call to get the number of dst buffers on hold
>- * @can_recycle: optional call to know if the codec is ready to recycle
>- * a dst buffer
>- * @recycle: optional call to tell the codec to recycle a dst buffer. Must go
>- * in pair with @can_recycle
>- * @drain: optional call if the codec has a custom way of draining
>- * @resume: optional call to resume after a resolution change
>- * @eos_sequence: optional call to get an end sequence to send to esparser
>- * for flush. Mutually exclusive with @drain.
>- * @isr: mandatory call when the ISR triggers
>- * @threaded_isr: mandatory call for the threaded ISR
>- */
>-struct amvdec_codec_ops {
>- int (*start)(struct amvdec_session *sess);
>- int (*stop)(struct amvdec_session *sess);
>- int (*load_extended_firmware)(struct amvdec_session *sess,
>- const u8 *data, u32 len);
>- u32 (*num_pending_bufs)(struct amvdec_session *sess);
>- int (*can_recycle)(struct amvdec_core *core);
>- void (*recycle)(struct amvdec_core *core, u32 buf_idx);
>- void (*drain)(struct amvdec_session *sess);
>- void (*resume)(struct amvdec_session *sess);
>- const u8 * (*eos_sequence)(u32 *len);
>- irqreturn_t (*isr)(struct amvdec_session *sess);
>- irqreturn_t (*threaded_isr)(struct amvdec_session *sess);
>-};
>-
>-/**
>- * struct amvdec_format - describes one of the OUTPUT (src) format supported
>- *
>- * @pixfmt: V4L2 pixel format
>- * @min_buffers: minimum amount of CAPTURE (dst) buffers
>- * @max_buffers: maximum amount of CAPTURE (dst) buffers
>- * @max_width: maximum picture width supported
>- * @max_height: maximum picture height supported
>- * @flags: enum flags associated with this pixfmt
>- * @vdec_ops: the VDEC operations that support this format
>- * @codec_ops: the codec operations that support this format
>- * @firmware_path: Path to the firmware that supports this format
>- * @pixfmts_cap: list of CAPTURE pixel formats available with pixfmt
>- */
>-struct amvdec_format {
>- u32 pixfmt;
>- u32 min_buffers;
>- u32 max_buffers;
>- u32 max_width;
>- u32 max_height;
>- u32 flags;
>-
>- struct amvdec_ops *vdec_ops;
>- struct amvdec_codec_ops *codec_ops;
>-
>- char *firmware_path;
>- u32 pixfmts_cap[4];
>-};
>-
>-enum amvdec_status {
>- STATUS_STOPPED,
>- STATUS_INIT,
>- STATUS_RUNNING,
>- STATUS_NEEDS_RESUME,
>-};
>-
>-/**
>- * struct amvdec_session - decoding session parameters
>- *
>- * @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
>- * @lock: cap & out queues lock
>- * @fmt_out: vdec pixel format for the OUTPUT queue
>- * @pixfmt_cap: V4L2 pixel format for the CAPTURE queue
>- * @src_buffer_size: size in bytes of the OUTPUT buffers' only plane
>- * @width: current picture width
>- * @height: current picture height
>- * @colorspace: current colorspace
>- * @ycbcr_enc: current ycbcr_enc
>- * @quantization: current quantization
>- * @xfer_func: current transfer function
>- * @pixelaspect: Pixel Aspect Ratio reported by the decoder
>- * @esparser_queued_bufs: number of buffers currently queued into ESPARSER
>- * @esparser_queue_work: work struct for the ESPARSER to process src buffers
>- * @streamon_cap: stream on flag for capture queue
>- * @streamon_out: stream on flag for output queue
>- * @sequence_cap: capture sequence counter
>- * @sequence_out: output sequence counter
>- * @should_stop: flag set if userspace signaled EOS via command
>- * or empty buffer
>- * @keyframe_found: flag set once a keyframe has been parsed
>- * @num_dst_bufs: number of destination buffers
>- * @changed_format: the format changed
>- * @canvas_alloc: array of all the canvas IDs allocated
>- * @canvas_num: number of canvas IDs allocated
>- * @vififo_vaddr: virtual address for the VIFIFO
>- * @vififo_paddr: physical address for the VIFIFO
>- * @vififo_size: size of the VIFIFO dma alloc
>- * @bufs_recycle: list of buffers that need to be recycled
>- * @bufs_recycle_lock: lock for the bufs_recycle list
>- * @recycle_thread: task struct for the recycling thread
>- * @timestamps: chronological list of src timestamps
>- * @ts_spinlock: spinlock for the timestamps list
>- * @last_irq_jiffies: tracks last time the vdec triggered an IRQ
>- * @last_offset: tracks last offset of vififo
>- * @wrap_count: number of times the vififo wrapped around
>- * @fw_idx_to_vb2_idx: firmware buffer index to vb2 buffer index
>- * @status: current decoding status
>- * @priv: codec private data
>- */
>-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;
>- struct mutex lock;
>-
>- const struct amvdec_format *fmt_out;
>- u32 pixfmt_cap;
>- u32 src_buffer_size;
>-
>- u32 width;
>- u32 height;
>- u32 colorspace;
>- u8 ycbcr_enc;
>- u8 quantization;
>- u8 xfer_func;
>-
>- struct v4l2_fract pixelaspect;
>-
>- atomic_t esparser_queued_bufs;
>- struct work_struct esparser_queue_work;
>-
>- unsigned int streamon_cap, streamon_out;
>- unsigned int sequence_cap, sequence_out;
>- unsigned int should_stop;
>- unsigned int keyframe_found;
>- unsigned int num_dst_bufs;
>- unsigned int changed_format;
>-
>- u8 canvas_alloc[MAX_CANVAS];
>- u32 canvas_num;
>-
>- void *vififo_vaddr;
>- dma_addr_t vififo_paddr;
>- u32 vififo_size;
>-
>- struct list_head bufs_recycle;
>- struct mutex bufs_recycle_lock; /* bufs_recycle list lock */
>- struct task_struct *recycle_thread;
>-
>- struct list_head timestamps;
>- spinlock_t ts_spinlock; /* timestamp list lock */
>-
>- u64 last_irq_jiffies;
>- u32 last_offset;
>- u32 wrap_count;
>- u32 fw_idx_to_vb2_idx[32];
>-
>- enum amvdec_status status;
>- void *priv;
>-};
>-
>-static inline struct amvdec_session *file_to_amvdec_session(struct file *filp)
>-{
>- return container_of(file_to_v4l2_fh(filp), struct amvdec_session, fh);
>-}
>-
>-u32 amvdec_get_output_size(struct amvdec_session *sess);
>-
>-#endif
>+/* SPDX-License-Identifier: GPL-2.0+ */
>+/*
>+ * Copyright (C) 2018 BayLibre, SAS
>+ * Author: Maxime Jourdan <mjourdan@baylibre.com>
>+ */
>+
>+#ifndef __MESON_VDEC_CORE_H_
>+#define __MESON_VDEC_CORE_H_
>+
>+#include <linux/irqreturn.h>
>+#include <linux/regmap.h>
>+#include <linux/list.h>
>+#include <media/videobuf2-v4l2.h>
>+#include <media/v4l2-ctrls.h>
>+#include <media/v4l2-device.h>
>+#include <linux/soc/amlogic/meson-canvas.h>
>+
>+#include "vdec_platform.h"
>+
>+/* 32 buffers in 3-plane YUV420 */
>+#define MAX_CANVAS (32 * 3)
>+
>+struct amvdec_buffer {
>+ struct list_head list;
>+ struct vb2_buffer *vb;
>+};
>+
>+/**
>+ * struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset
>+ *
>+ * @list: used to make lists out of this struct
>+ * @tc: timecode from the v4l2 buffer
>+ * @ts: timestamp from the VB2 buffer
>+ * @offset: offset in the VIFIFO where the associated packet was written
>+ * @flags: flags from the v4l2 buffer
>+ * @used_count: times this timestamp was checked for a match with a dst buffer
>+ */
>+struct amvdec_timestamp {
>+ struct list_head list;
>+ struct v4l2_timecode tc;
>+ u64 ts;
>+ u32 offset;
>+ u32 flags;
>+ u32 used_count;
>+};
>+
>+struct amvdec_session;
>+
>+/**
>+ * struct amvdec_core - device parameters, singleton
>+ *
>+ * @dos_base: DOS memory base address
>+ * @esparser_base: PARSER memory base address
>+ * @regmap_ao: regmap for the AO bus
>+ * @dev: core device
>+ * @dev_dec: decoder device
>+ * @platform: platform-specific data
>+ * @canvas: canvas provider reference
>+ * @dos_parser_clk: DOS_PARSER clock
>+ * @dos_clk: DOS clock
>+ * @vdec_1_clk: VDEC_1 clock
>+ * @vdec_hevc_clk: VDEC_HEVC clock
>+ * @vdec_hevcf_clk: VDEC_HEVCF clock
>+ * @esparser_reset: RESET for the PARSER
>+ * @vdev_dec: video device for the decoder
>+ * @v4l2_dev: v4l2 device
>+ * @m2m_dev: v4l2 m2m device
>+ * @cur_sess: current decoding session
>+ * @lock: video device lock
>+ * @irq: decoder irq
>+ */
>+struct amvdec_core {
>+ void __iomem *dos_base;
>+ void __iomem *esparser_base;
>+ struct regmap *regmap_ao;
>+
>+ struct device *dev;
>+ struct device *dev_dec;
>+ const struct vdec_platform *platform;
>+
>+ struct meson_canvas *canvas;
>+
>+ struct clk *dos_parser_clk;
>+ struct clk *dos_clk;
>+ struct clk *vdec_1_clk;
>+ struct clk *vdec_hevc_clk;
>+ struct clk *vdec_hevcf_clk;
>+
>+ struct reset_control *esparser_reset;
>+
>+ struct video_device *vdev_dec;
>+ struct v4l2_device v4l2_dev;
>+ struct v4l2_m2m_dev *m2m_dev;
>+
>+ struct amvdec_session *cur_sess;
>+ struct mutex lock;
>+ int irq;
>+};
>+
>+/**
>+ * struct amvdec_ops - vdec operations
>+ *
>+ * @start: mandatory call when the vdec needs to initialize
>+ * @stop: mandatory call when the vdec needs to stop
>+ * @conf_esparser: mandatory call to let the vdec configure the ESPARSER
>+ * @vififo_level: mandatory call to get the current amount of data
>+ * in the VIFIFO
>+ */
>+struct amvdec_ops {
>+ int (*start)(struct amvdec_session *sess);
>+ int (*stop)(struct amvdec_session *sess);
>+ void (*conf_esparser)(struct amvdec_session *sess);
>+ u32 (*vififo_level)(struct amvdec_session *sess);
>+};
>+
>+/**
>+ * struct amvdec_codec_ops - codec operations
>+ *
>+ * @start: mandatory call when the codec needs to initialize
>+ * @stop: mandatory call when the codec needs to stop
>+ * @load_extended_firmware: optional call to load additional firmware bits
>+ * @num_pending_bufs: optional call to get the number of dst buffers on hold
>+ * @can_recycle: optional call to know if the codec is ready to recycle
>+ * a dst buffer
>+ * @recycle: optional call to tell the codec to recycle a dst buffer. Must go
>+ * in pair with @can_recycle
>+ * @drain: optional call if the codec has a custom way of draining
>+ * @resume: optional call to resume after a resolution change
>+ * @eos_sequence: optional call to get an end sequence to send to esparser
>+ * for flush. Mutually exclusive with @drain.
>+ * @isr: mandatory call when the ISR triggers
>+ * @threaded_isr: mandatory call for the threaded ISR
>+ */
>+struct amvdec_codec_ops {
>+ int (*start)(struct amvdec_session *sess);
>+ int (*stop)(struct amvdec_session *sess);
>+ int (*load_extended_firmware)(struct amvdec_session *sess,
>+ const u8 *data, u32 len);
>+ u32 (*num_pending_bufs)(struct amvdec_session *sess);
>+ int (*can_recycle)(struct amvdec_core *core);
>+ void (*recycle)(struct amvdec_core *core, u32 buf_idx);
>+ void (*drain)(struct amvdec_session *sess);
>+ void (*resume)(struct amvdec_session *sess);
>+ const u8 * (*eos_sequence)(u32 *len);
>+ irqreturn_t (*isr)(struct amvdec_session *sess);
>+ irqreturn_t (*threaded_isr)(struct amvdec_session *sess);
>+};
>+
>+/**
>+ * struct amvdec_format - describes one of the OUTPUT (src) format supported
>+ *
>+ * @pixfmt: V4L2 pixel format
>+ * @min_buffers: minimum amount of CAPTURE (dst) buffers
>+ * @max_buffers: maximum amount of CAPTURE (dst) buffers
>+ * @max_width: maximum picture width supported
>+ * @max_height: maximum picture height supported
>+ * @flags: enum flags associated with this pixfmt
>+ * @vdec_ops: the VDEC operations that support this format
>+ * @codec_ops: the codec operations that support this format
>+ * @firmware_path: Path to the firmware that supports this format
>+ * @pixfmts_cap: list of CAPTURE pixel formats available with pixfmt
>+ */
>+struct amvdec_format {
>+ u32 pixfmt;
>+ u32 min_buffers;
>+ u32 max_buffers;
>+ u32 max_width;
>+ u32 max_height;
>+ u32 flags;
>+
>+ struct amvdec_ops *vdec_ops;
>+ struct amvdec_codec_ops *codec_ops;
>+
>+ char *firmware_path;
>+ u32 pixfmts_cap[4];
>+};
>+
>+enum amvdec_status {
>+ STATUS_STOPPED,
>+ STATUS_INIT,
>+ STATUS_RUNNING,
>+ STATUS_NEEDS_RESUME,
>+};
>+
>+/**
>+ * struct amvdec_session - decoding session parameters
>+ *
>+ * @core: reference to the vdec core struct
>+ * @fh: v4l2 file handle
>+ * @m2m_ctx: v4l2 m2m context
>+ * @ctrl_handler: V4L2 control handler
>+ * @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE
>+ * @lock: cap & out queues lock
>+ * @fmt_out: vdec pixel format for the OUTPUT queue
>+ * @pixfmt_cap: V4L2 pixel format for the CAPTURE queue
>+ * @src_buffer_size: size in bytes of the OUTPUT buffers' only plane
>+ * @width: current picture width
>+ * @height: current picture height
>+ * @colorspace: current colorspace
>+ * @ycbcr_enc: current ycbcr_enc
>+ * @quantization: current quantization
>+ * @xfer_func: current transfer function
>+ * @pixelaspect: Pixel Aspect Ratio reported by the decoder
>+ * @esparser_queued_bufs: number of buffers currently queued into ESPARSER
>+ * @esparser_queue_work: work struct for the ESPARSER to process src buffers
>+ * @streamon_cap: stream on flag for capture queue
>+ * @streamon_out: stream on flag for output queue
>+ * @sequence_cap: capture sequence counter
>+ * @sequence_out: output sequence counter
>+ * @should_stop: flag set if userspace signaled EOS via command
>+ * or empty buffer
>+ * @keyframe_found: flag set once a keyframe has been parsed
>+ * @num_dst_bufs: number of destination buffers
>+ * @changed_format: the format changed
>+ * @canvas_alloc: array of all the canvas IDs allocated
>+ * @canvas_num: number of canvas IDs allocated
>+ * @vififo_vaddr: virtual address for the VIFIFO
>+ * @vififo_paddr: physical address for the VIFIFO
>+ * @vififo_size: size of the VIFIFO dma alloc
>+ * @bufs_recycle: list of buffers that need to be recycled
>+ * @bufs_recycle_lock: lock for the bufs_recycle list
>+ * @recycle_thread: task struct for the recycling thread
>+ * @timestamps: chronological list of src timestamps
>+ * @ts_spinlock: spinlock for the timestamps list
>+ * @last_irq_jiffies: tracks last time the vdec triggered an IRQ
>+ * @last_offset: tracks last offset of vififo
>+ * @wrap_count: number of times the vififo wrapped around
>+ * @fw_idx_to_vb2_idx: firmware buffer index to vb2 buffer index
>+ * @status: current decoding status
>+ * @priv: codec private data
>+ */
>+struct amvdec_session {
>+ struct amvdec_core *core;
>+
>+ struct v4l2_fh fh;
>+ struct v4l2_m2m_ctx *m2m_ctx;
>+ struct v4l2_ctrl_handler ctrl_handler;
>+ struct v4l2_ctrl *ctrl_min_buf_capture;
>+ struct mutex lock;
>+
>+ const struct amvdec_format *fmt_out;
>+ u32 pixfmt_cap;
>+ u32 src_buffer_size;
>+
>+ u32 width;
>+ u32 height;
>+ u32 colorspace;
>+ u8 ycbcr_enc;
>+ u8 quantization;
>+ u8 xfer_func;
>+
>+ struct v4l2_fract pixelaspect;
>+
>+ atomic_t esparser_queued_bufs;
>+ struct work_struct esparser_queue_work;
>+
>+ unsigned int streamon_cap, streamon_out;
>+ unsigned int sequence_cap, sequence_out;
>+ unsigned int should_stop;
>+ unsigned int keyframe_found;
>+ unsigned int num_dst_bufs;
>+ unsigned int changed_format;
>+
>+ u8 canvas_alloc[MAX_CANVAS];
>+ u32 canvas_num;
>+
>+ void *vififo_vaddr;
>+ dma_addr_t vififo_paddr;
>+ u32 vififo_size;
>+
>+ struct list_head bufs_recycle;
>+ struct mutex bufs_recycle_lock; /* bufs_recycle list lock */
>+ struct task_struct *recycle_thread;
>+
>+ struct list_head timestamps;
>+ spinlock_t ts_spinlock; /* timestamp list lock */
>+
>+ u64 last_irq_jiffies;
>+ u32 last_offset;
>+ u32 wrap_count;
>+ u32 fw_idx_to_vb2_idx[32];
>+
>+ enum amvdec_status status;
>+ void *priv;
>+};
>+
>+static inline struct amvdec_session *file_to_amvdec_session(struct file *filp)
>+{
>+ return container_of(file_to_v4l2_fh(filp), struct amvdec_session, fh);
>+}
>+
>+u32 amvdec_get_output_size(struct amvdec_session *sess);
>+
>+#endif
What is this huge diff?
>--
>2.54.0.windows.1
>
>
>_______________________________________________
>linux-amlogic mailing list
>linux-amlogic@lists.infradead.org
>http://lists.infradead.org/mailman/listinfo/linux-amlogic
Best regards,
Ferass
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
2026-07-13 18:16 ` Alfie Varghese
@ 2026-07-14 7:15 ` Dan Carpenter
-1 siblings, 0 replies; 11+ messages in thread
From: Dan Carpenter @ 2026-07-14 7:15 UTC (permalink / raw)
To: Alfie Varghese
Cc: neil.armstrong, gregkh, linux-media, linux-amlogic, linux-staging,
linux-kernel
Please slow down. Wait 24 hours between resends. Otherwise it's like
we are hovering over you watching you work and holding your hand for
the tricky bits. It is overwhelming for maintainers.
Follow the other v2 rules as well:
https://staticthinking.wordpress.com/2022/07/27/how-to-send-a-v2-patch/
Also say that you are using AI and haven't tested these.
regards,
dan carpenter
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
@ 2026-07-14 7:15 ` Dan Carpenter
0 siblings, 0 replies; 11+ messages in thread
From: Dan Carpenter @ 2026-07-14 7:15 UTC (permalink / raw)
To: Alfie Varghese
Cc: neil.armstrong, gregkh, linux-media, linux-amlogic, linux-staging,
linux-kernel
Please slow down. Wait 24 hours between resends. Otherwise it's like
we are hovering over you watching you work and holding your hand for
the tricky bits. It is overwhelming for maintainers.
Follow the other v2 rules as well:
https://staticthinking.wordpress.com/2022/07/27/how-to-send-a-v2-patch/
Also say that you are using AI and haven't tested these.
regards,
dan carpenter
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
2026-07-13 18:16 ` Alfie Varghese
@ 2026-07-14 13:59 ` kernel test robot
-1 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2026-07-14 13:59 UTC (permalink / raw)
To: Alfie Varghese, neil.armstrong, gregkh, linux-media,
linux-amlogic, linux-staging, linux-kernel
Cc: oe-kbuild-all, Alfie Varghese
Hi Alfie,
kernel test robot noticed the following build errors:
[auto build test ERROR on staging/staging-testing]
url: https://github.com/intel-lab-lkp/linux/commits/Alfie-Varghese/staging-media-meson-vdec-fix-advanced-concurrency-and-UAF-bugs/20260714-023427
base: staging/staging-testing
patch link: https://lore.kernel.org/r/20260713181602.773-1-alfievarghese22%40gmail.com
patch subject: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
config: parisc-allmodconfig (https://download.01.org/0day-ci/archive/20260714/202607142142.HnOYDndp-lkp@intel.com/config)
compiler: hppa-linux-gcc (GCC) 16.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260714/202607142142.HnOYDndp-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202607142142.HnOYDndp-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/staging/media/meson/vdec/vdec.c: In function 'vdec_m2m_job_abort':
>> drivers/staging/media/meson/vdec/vdec.c:156:33: error: 'struct amvdec_session' has no member named 'm2m_dev'
156 | v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
| ^~
vim +156 drivers/staging/media/meson/vdec/vdec.c
3e7f51bd96077ac Maxime Jourdan 2019-06-06 151
3e7f51bd96077ac Maxime Jourdan 2019-06-06 152 static void vdec_m2m_job_abort(void *priv)
3e7f51bd96077ac Maxime Jourdan 2019-06-06 153 {
3e7f51bd96077ac Maxime Jourdan 2019-06-06 154 struct amvdec_session *sess = priv;
3e7f51bd96077ac Maxime Jourdan 2019-06-06 155
3e7f51bd96077ac Maxime Jourdan 2019-06-06 @156 v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
3e7f51bd96077ac Maxime Jourdan 2019-06-06 157 }
3e7f51bd96077ac Maxime Jourdan 2019-06-06 158
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
@ 2026-07-14 13:59 ` kernel test robot
0 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2026-07-14 13:59 UTC (permalink / raw)
To: Alfie Varghese, neil.armstrong, gregkh, linux-media,
linux-amlogic, linux-staging, linux-kernel
Cc: oe-kbuild-all, Alfie Varghese
Hi Alfie,
kernel test robot noticed the following build errors:
[auto build test ERROR on staging/staging-testing]
url: https://github.com/intel-lab-lkp/linux/commits/Alfie-Varghese/staging-media-meson-vdec-fix-advanced-concurrency-and-UAF-bugs/20260714-023427
base: staging/staging-testing
patch link: https://lore.kernel.org/r/20260713181602.773-1-alfievarghese22%40gmail.com
patch subject: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
config: parisc-allmodconfig (https://download.01.org/0day-ci/archive/20260714/202607142142.HnOYDndp-lkp@intel.com/config)
compiler: hppa-linux-gcc (GCC) 16.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260714/202607142142.HnOYDndp-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202607142142.HnOYDndp-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/staging/media/meson/vdec/vdec.c: In function 'vdec_m2m_job_abort':
>> drivers/staging/media/meson/vdec/vdec.c:156:33: error: 'struct amvdec_session' has no member named 'm2m_dev'
156 | v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
| ^~
vim +156 drivers/staging/media/meson/vdec/vdec.c
3e7f51bd96077ac Maxime Jourdan 2019-06-06 151
3e7f51bd96077ac Maxime Jourdan 2019-06-06 152 static void vdec_m2m_job_abort(void *priv)
3e7f51bd96077ac Maxime Jourdan 2019-06-06 153 {
3e7f51bd96077ac Maxime Jourdan 2019-06-06 154 struct amvdec_session *sess = priv;
3e7f51bd96077ac Maxime Jourdan 2019-06-06 155
3e7f51bd96077ac Maxime Jourdan 2019-06-06 @156 v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
3e7f51bd96077ac Maxime Jourdan 2019-06-06 157 }
3e7f51bd96077ac Maxime Jourdan 2019-06-06 158
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
2026-07-13 18:16 ` Alfie Varghese
@ 2026-07-14 14:23 ` kernel test robot
-1 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2026-07-14 14:23 UTC (permalink / raw)
To: Alfie Varghese, neil.armstrong, gregkh, linux-media,
linux-amlogic, linux-staging, linux-kernel
Cc: llvm, oe-kbuild-all, Alfie Varghese
Hi Alfie,
kernel test robot noticed the following build errors:
[auto build test ERROR on staging/staging-testing]
url: https://github.com/intel-lab-lkp/linux/commits/Alfie-Varghese/staging-media-meson-vdec-fix-advanced-concurrency-and-UAF-bugs/20260714-023427
base: staging/staging-testing
patch link: https://lore.kernel.org/r/20260713181602.773-1-alfievarghese22%40gmail.com
patch subject: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20260714/202607142224.5HqGONfe-lkp@intel.com/config)
compiler: clang version 23.0.0git (https://github.com/llvm/llvm-project b3e6e6dabdc02153552a64fc74ff5c7532447eed)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260714/202607142224.5HqGONfe-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202607142224.5HqGONfe-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/staging/media/meson/vdec/vdec.c:156:28: error: no member named 'm2m_dev' in 'struct amvdec_session'
156 | v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
| ~~~~ ^
1 error generated.
vim +156 drivers/staging/media/meson/vdec/vdec.c
3e7f51bd96077a Maxime Jourdan 2019-06-06 151
3e7f51bd96077a Maxime Jourdan 2019-06-06 152 static void vdec_m2m_job_abort(void *priv)
3e7f51bd96077a Maxime Jourdan 2019-06-06 153 {
3e7f51bd96077a Maxime Jourdan 2019-06-06 154 struct amvdec_session *sess = priv;
3e7f51bd96077a Maxime Jourdan 2019-06-06 155
3e7f51bd96077a Maxime Jourdan 2019-06-06 @156 v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
3e7f51bd96077a Maxime Jourdan 2019-06-06 157 }
3e7f51bd96077a Maxime Jourdan 2019-06-06 158
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
@ 2026-07-14 14:23 ` kernel test robot
0 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2026-07-14 14:23 UTC (permalink / raw)
To: Alfie Varghese, neil.armstrong, gregkh, linux-media,
linux-amlogic, linux-staging, linux-kernel
Cc: llvm, oe-kbuild-all, Alfie Varghese
Hi Alfie,
kernel test robot noticed the following build errors:
[auto build test ERROR on staging/staging-testing]
url: https://github.com/intel-lab-lkp/linux/commits/Alfie-Varghese/staging-media-meson-vdec-fix-advanced-concurrency-and-UAF-bugs/20260714-023427
base: staging/staging-testing
patch link: https://lore.kernel.org/r/20260713181602.773-1-alfievarghese22%40gmail.com
patch subject: [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs
config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20260714/202607142224.5HqGONfe-lkp@intel.com/config)
compiler: clang version 23.0.0git (https://github.com/llvm/llvm-project b3e6e6dabdc02153552a64fc74ff5c7532447eed)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260714/202607142224.5HqGONfe-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202607142224.5HqGONfe-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/staging/media/meson/vdec/vdec.c:156:28: error: no member named 'm2m_dev' in 'struct amvdec_session'
156 | v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
| ~~~~ ^
1 error generated.
vim +156 drivers/staging/media/meson/vdec/vdec.c
3e7f51bd96077a Maxime Jourdan 2019-06-06 151
3e7f51bd96077a Maxime Jourdan 2019-06-06 152 static void vdec_m2m_job_abort(void *priv)
3e7f51bd96077a Maxime Jourdan 2019-06-06 153 {
3e7f51bd96077a Maxime Jourdan 2019-06-06 154 struct amvdec_session *sess = priv;
3e7f51bd96077a Maxime Jourdan 2019-06-06 155
3e7f51bd96077a Maxime Jourdan 2019-06-06 @156 v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
3e7f51bd96077a Maxime Jourdan 2019-06-06 157 }
3e7f51bd96077a Maxime Jourdan 2019-06-06 158
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-07-14 14:24 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13 18:16 [PATCH v2] staging: media: meson: vdec: fix advanced concurrency and UAF bugs Alfie Varghese
2026-07-13 18:16 ` Alfie Varghese
2026-07-13 18:33 ` sashiko-bot
2026-07-13 19:37 ` Ferass El Hafidi
2026-07-13 19:37 ` Ferass El Hafidi
2026-07-14 7:15 ` Dan Carpenter
2026-07-14 7:15 ` Dan Carpenter
2026-07-14 13:59 ` kernel test robot
2026-07-14 13:59 ` kernel test robot
2026-07-14 14:23 ` kernel test robot
2026-07-14 14:23 ` kernel test robot
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.