Commit c0c7f3366a for ffmpeg
commit c0c7f3366ab5ae09dcfc3573fc8628544f70340f
Author: Michael Niedermayer <michael@niedermayer.cc>
Date: Tue Nov 3 16:28:42 2026 +0100
avcodec/v4l2_buffers: copy the frame planes into multi-plane buffers row by row
The copy for the "M" pixel formats took each plane from frame->buf[i],
assuming one buffer per plane holding exactly the driver's layout. A
frame with all planes in one buffer, as the rawvideo decoder produces,
has no buf[1] and crashed; cropped frames copied the wrong area and
every frame copied its padding. Copy the visible rows of each plane with
the driver's stride into a plane checked to hold them, as the single
buffer path does.
Fixes: wBGqpiZHMvPm
Fixes: null pointer dereference
Null pointer dereference Replicated through UnModified FFmpeg with ASAN
using a stub V4L2 mem2mem encoder with an NV12M output format and a
rawvideo input
Found during triage/review of the security report AISLE-2026-R0043
diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c
index 9ad9aebde0..b7c528dee9 100644
--- a/libavcodec/v4l2_buffers.c
+++ b/libavcodec/v4l2_buffers.c
@@ -471,9 +471,44 @@ static int v4l2_buffer_swframe_to_single_buffer(const AVFrame *frame,
return 0;
}
+static int v4l2_buffer_swframe_to_planes(const AVFrame *frame, V4L2Buffer *out)
+{
+ const struct v4l2_pix_format_mplane *pix_mp = &out->context->format.fmt.pix_mp;
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
+ int height = pix_mp->height;
+ int bytewidths[4];
+ int ret;
+
+ if (!desc || !V4L2_TYPE_IS_MULTIPLANAR(out->buf.type) ||
+ out->num_planes > av_pix_fmt_count_planes(frame->format) ||
+ frame->width > pix_mp->width || frame->height > height)
+ return AVERROR(EINVAL);
+
+ ret = av_image_fill_linesizes(bytewidths, frame->format, frame->width);
+ if (ret < 0)
+ return ret;
+
+ for (int i = 0; i < out->num_planes; i++) {
+ int shift = i == 1 || i == 2 ? desc->log2_chroma_h : 0;
+ size_t stride = pix_mp->plane_fmt[i].bytesperline;
+ size_t size = stride * AV_CEIL_RSHIFT(height, shift);
+
+ if (!frame->data[i] || stride < bytewidths[i] ||
+ size > out->plane_info[i].length)
+ return AVERROR(EINVAL);
+
+ av_image_copy_plane(out->plane_info[i].mm_addr, stride,
+ frame->data[i], frame->linesize[i], bytewidths[i],
+ AV_CEIL_RSHIFT(frame->height, shift));
+ out->planes[i].bytesused = size;
+ out->planes[i].length = out->plane_info[i].length;
+ }
+
+ return 0;
+}
+
static int v4l2_buffer_swframe_to_buf(const AVFrame *frame, V4L2Buffer *out)
{
- int i, ret;
struct v4l2_format fmt = out->context->format;
int pixel_format = V4L2_TYPE_IS_MULTIPLANAR(fmt.type) ?
fmt.fmt.pix_mp.pixelformat : fmt.fmt.pix.pixelformat;
@@ -521,13 +556,7 @@ static int v4l2_buffer_swframe_to_buf(const AVFrame *frame, V4L2Buffer *out)
height, bytesperline,
sizeimage);
- for (i = 0; i < out->num_planes; i++) {
- ret = v4l2_bufref_to_buf(out, i, frame->buf[i]->data, frame->buf[i]->size, 0);
- if (ret)
- return ret;
- }
-
- return 0;
+ return v4l2_buffer_swframe_to_planes(frame, out);
}
/******************************************************************************