[PATCH v7 09/19] media: meson: vdec: Fix vp9 header update failure on invalid payloads

sashiko-bot at kernel.org sashiko-bot at kernel.org
Mon Jul 13 05:42:50 PDT 2026


Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:

New issues:
- [Critical] Negative error code converted to massive positive integer bypasses validation, leading to out-of-bounds DMA fetch and memory corruption.
- [High] Early return from `esparser_isr` bypasses hardware interrupt clearing, leading to an unrecoverable IRQ storm and system lockup.
- [High] Using `break` inside `scoped_guard` escapes only the guard mechanism, causing an infinite busy-wait loop when the worker queue is empty.

Pre-existing issues:
- [Critical] Implicit integer promotion of VP9 frame sizes allows crafting negative sizes to bypass bounds checks, causing a massive out-of-bounds heap copy.
- [High] esparser worker accesses hardware registers and waitqueues without taking the global lock, causing hardware state corruption across concurrent sessions.
- [High] VP9 frame size validation checks against maximum buffer capacity instead of actual payload size, allowing information disclosure of uninitialized kernel memory.
--

--- Patch [9]: [PATCH v7 09/19] media: meson: vdec: Fix vp9 header update failure on invalid payloads ---
commit e42c61ef1ea850aa0156d3301824f682854a42da
Author: Anand Moon <linux.amoon at gmail.com>

media: meson: vdec: Fix vp9 header update failure on invalid payloads

Ensure vp9_update_header() returns an explicit error code on invalid or
malformed buffer payloads instead of silently returning zero.

> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
> @@ -97,11 +97,15 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
>  	unsigned char *old_header = NULL;
>  
>  	dp = (uint8_t *)vb2_plane_vaddr(buf, 0);
> +	if (!dp)
> +		return -EINVAL;

[Severity: Critical]
Does returning a negative error code here cause an issue in the caller?

Looking at esparser_queue(), it assigns the return value of 
vp9_update_header() to payload_size and checks if (payload_size == 0). If 
this returns -EINVAL and the caller assigns the result to a u32 variable, 
could the negative error code underflow to a massive positive integer (like 
4294967274)? 

This might completely bypass validation and be passed to 
esparser_pad_start_code(), causing an out-of-bounds memset and out-of-bounds 
DMA commands to the hardware.

>  	dsize = vb2_get_plane_payload(buf, 0);
>  
> -	if (dsize == vb2_plane_size(buf, 0)) {
> -		dev_warn(core->dev, "%s: unable to update header\n", __func__);
> -		return 0;
> +	if (dsize <= 0 || dsize > vb2_plane_size(buf, 0)) {
> +		dev_warn(core->dev, "%s: invalid payload size %d\n",
> +			 __func__, dsize);
> +		return -EINVAL;
>  	}
>  

[ ... ]

> @@ -109,13 +113,16 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
>  		num_frames = (marker & 0x7) + 1;
>  		mag = ((marker >> 3) & 0x3) + 1;
>  		mag_ptr = dsize - mag * num_frames - 2;
> -		if (dp[mag_ptr] != marker)
> -			return 0;
> +		if (mag_ptr < 0 || dp[mag_ptr] != marker)
> +			return -EINVAL;
>  
>  		mag_ptr++;
>  		for (cur_frame = 0; cur_frame < num_frames; cur_frame++) {
>  			frame_size[cur_frame] = 0;
>  			for (cur_mag = 0; cur_mag < mag; cur_mag++) {
> +				if (mag_ptr >= dsize)
> +					return -EINVAL;
> +
>  				frame_size[cur_frame] |=
>  					(dp[mag_ptr] << (cur_mag * 8));

[Severity: Critical]
This isn't a bug introduced by this patch, but does this shift operation 
cause an implicit sign extension?

Because dp is a pointer to u8, the byte at dp[mag_ptr] undergoes integer 
promotion to a signed int before shifting. If cur_mag is 3 and the byte is 
128 or larger, shifting it by 24 bits produces a negative value due to the 
sign bit.

Could this allow crafting combinations of large positive and negative 
frame_size values so their sum wraps around to a small positive integer, 
bypassing the bounds check later on and causing a massive out-of-bounds 
memmove()?

>  				mag_ptr++;
>  			}

[ ... ]

> @@ -140,7 +147,7 @@ static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
>  
>  	if (new_frame_size >= vb2_plane_size(buf, 0)) {
>  		dev_warn(core->dev, "%s: unable to update header\n", __func__);
> -		return 0;
> +		return -ENOMEM;
>  	}

[Severity: High]
This isn't a bug introduced by this patch, but does validating against the 
total buffer capacity instead of the actual payload size allow information 
disclosure?

If the check uses vb2_plane_size(buf, 0) instead of the actual received 
payload length in dsize, could a valid short payload with a crafted 
superframe index declare frame sizes that exceed the payload length but 
remain under the total buffer capacity?

If so, memmove() could copy trailing uninitialized or stale kernel memory 
from the buffer into the active payload stream.


Outside of the immediate diff, there are a few other questions about the 
esparser implementation that may be worth reviewing:

[Severity: High]
This isn't a bug introduced by this patch, but does esparser_queue_all_src() 
queue hardware operations without acquiring the global hardware lock?

The worker runs concurrently for multiple sessions because the work queue is 
initialized per-session. The worker locks the per-session sess->lock but 
does not appear to acquire the global core->lock or verify hardware ownership 
before calling esparser_write_data():

esparser_queue_all_src() {
    ...
    ret = esparser_queue(sess, vbuf);
    ...
}

Could multiple sessions invoke esparser_write_data() simultaneously, causing 
conflicting writes to the hardware parser registers and corruption of the 
global search_done waitqueue flag?

[Severity: High]
Could the early return in esparser_isr() bypass clearing the hardware 
interrupt?

The interrupt service routine starts with a check:

esparser_isr() {
    if (!sess)
        return IRQ_HANDLED;
    ...
}

If a hardware interrupt asserts just as the session is torn down, returning 
early leaves the hardware interrupt un-cleared. Would this cause the 
hardware to hold the interrupt line asserted indefinitely, leading to an 
unrecoverable IRQ storm and system lockup?

[Severity: High]
Does the break statement inside esparser_queue_all_src() properly exit the 
worker loop?

When checking if a buffer is available inside a scoped_guard():

esparser_queue_all_src() {
    ...
    vbuf = v4l2_m2m_next_src_buf(sess->m2m_ctx);
    if (!vbuf)
        break;
    ...
}

Since scoped_guard() is implemented internally as a for loop, breaking here 
would only escape the guard's inner loop. If processed_frame remains false, 
the outer while (1) loop proceeds past its own exit condition:

    if (processed_frame)
        break;
    /* Give other threads and IRQ routines a window ... */
    cond_resched();

Could this pin a CPU core at 100% in an infinite busy-wait loop when the 
queue is empty?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=9



More information about the linux-amlogic mailing list