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
This commit is contained in:
Michael Niedermayer authored and michaelni committed 2026-11-05 01:51:07 +00:00
1 parent 568b3c5e31
commit c0c7f3366a
1 file changed
+37 -8
+37 -8
View File
@@ -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);
}
/******************************************************************************