Commit fdd4e83ea0 for ffmpeg
commit fdd4e83ea06e426558ded06f6b8d6ed9f752da21
Author: Prasanna Dabi <dabi.prasanna@gmail.com>
Date: Tue Oct 20 07:49:28 2026 +0000
avutil/hwcontext_vulkan: copy only the bytes of the visible rows
copy_buffer_data() copied FFABS(linesize) bytes per row between the
software frame and the staging buffer. The last row of a plane does not
have to extend to the full linesize, so this read past the end of the
software frame on upload and wrote past it on download.
The staging path is always taken for frames with a negative linesize,
and by default for multi-plane formats on the NVIDIA proprietary driver,
where host image copies are disabled and host memory import is avoided.
Same issue as fixed for hwcontext_cuda in f07f02abb864.
Out of array read and write replicated through the API with ASAN.
diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 8e17f258f0..0ddfafb1b8 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -4725,6 +4725,15 @@ static int copy_buffer_data(AVHWFramesContext *hwfc, FFVkBuffer *vkbuf,
{
int err;
VulkanDevicePriv *p = hwfc->device_ctx->hwctx;
+ int bytewidth[AV_NUM_DATA_POINTERS];
+
+ /* Copy only the visible bytes of each row, the last row of a plane
+ * does not have to extend to the full linesize. */
+ for (int i = 0; i < planes; i++) {
+ bytewidth[i] = av_image_get_linesize(swf->format, swf->width, i);
+ if (bytewidth[i] < 0)
+ return bytewidth[i];
+ }
if (upload) {
for (int i = 0; i < planes; i++)
@@ -4732,7 +4741,7 @@ static int copy_buffer_data(AVHWFramesContext *hwfc, FFVkBuffer *vkbuf,
region[i].bufferRowLength,
swf->data[i],
swf->linesize[i],
- FFABS(swf->linesize[i]),
+ bytewidth[i],
region[i].imageExtent.height);
err = ff_vk_flush_buffer(&p->vkctx, vkbuf, 0, VK_WHOLE_SIZE, 1);
@@ -4754,7 +4763,7 @@ static int copy_buffer_data(AVHWFramesContext *hwfc, FFVkBuffer *vkbuf,
swf->linesize[i],
vkbuf->mapped_mem + region[i].bufferOffset,
region[i].bufferRowLength,
- FFABS(swf->linesize[i]),
+ bytewidth[i],
region[i].imageExtent.height);
}