summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2020-09-17 15:20:08 +0200
committerwm4 <wm4@nowhere>2020-09-17 15:24:27 +0200
commit083adf97e14f3996f3c5c54b418de9cc5905b027 (patch)
tree35b599efe63eb4c31cccee0539bf8355a610796d /video
parentb2f4320d06304f16a3f447e7e68cab54b670bef1 (diff)
downloadmpv-083adf97e14f3996f3c5c54b418de9cc5905b027.tar.bz2
mpv-083adf97e14f3996f3c5c54b418de9cc5905b027.tar.xz
sws_utils: work around libswscale corrupting memory yet again
If the alignment is less than 16, certain libswscale code paths will silently corrupt memory outside of the target buffer. This actually affected the libmpv software rendering API (that was fun to debug). Rather than passing this problem to the next API user, try to avoid it within libmpv. It's unclear which alignment libswscale requires for safe operation. I'm picking 32 (one more than the observed safe value in the case I experienced), because libavfilter mostly uses this value. The way to work this around is slow: just make a full copy of the entire input or output image. Possibly this could be optimized by using the slice API, but that would be more effort, and would likely expose further libswscale bugs. Hope that this is a rarely needed path. The next commit will update the alignment requirement documentation bits.
Diffstat (limited to 'video')
-rw-r--r--video/sws_utils.c58
-rw-r--r--video/sws_utils.h1
2 files changed, 57 insertions, 2 deletions
diff --git a/video/sws_utils.c b/video/sws_utils.c
index 87ac82626c..4945734f4f 100644
--- a/video/sws_utils.c
+++ b/video/sws_utils.c
@@ -177,6 +177,8 @@ static void free_mp_sws(void *p)
sws_freeContext(ctx->sws);
sws_freeFilter(ctx->src_filter);
sws_freeFilter(ctx->dst_filter);
+ TA_FREEP(&ctx->aligned_src);
+ TA_FREEP(&ctx->aligned_dst);
}
// You're supposed to set your scaling parameters on the returned context.
@@ -234,6 +236,8 @@ int mp_sws_reinit(struct mp_sws_context *ctx)
sws_freeContext(ctx->sws);
ctx->sws = NULL;
ctx->zimg_ok = false;
+ TA_FREEP(&ctx->aligned_src);
+ TA_FREEP(&ctx->aligned_dst);
#if HAVE_ZIMG
if (allow_zimg(ctx)) {
@@ -325,6 +329,42 @@ success:
return 1;
}
+static struct mp_image *check_alignment(struct mp_log *log,
+ struct mp_image **alloc,
+ struct mp_image *img)
+{
+ // It's completely unclear which alignment libswscale wants (for performance)
+ // or requires (for avoiding crashes and memory corruption).
+ // Is it av_cpu_max_align()? Is it the hardcoded AVFrame "default" of 32
+ // in get_video_buffer()? Is it whatever avcodec_align_dimensions2()
+ // determines? It's like you can't win if you try to prevent libswscale from
+ // corrupting memory...
+ // So use 32, a value that has been experimentally determined to be safe,
+ // and which in most cases is not larger than decoder output. It is smaller
+ // or equal to what most image allocators in mpv/ffmpeg use.
+ size_t align = 32;
+ assert(align <= MP_IMAGE_BYTE_ALIGN); // or mp_image_alloc will not cut it
+
+ bool is_aligned = true;
+ for (int p = 0; p < img->num_planes; p++) {
+ is_aligned &= MP_IS_ALIGNED((uintptr_t)img->planes[p], align);
+ is_aligned &= MP_IS_ALIGNED(labs(img->stride[p]), align);
+ }
+
+ if (is_aligned)
+ return img;
+
+ if (!*alloc) {
+ mp_verbose(log, "unaligned libswscale parameter; using slow copy.\n");
+ *alloc = mp_image_alloc(img->imgfmt, img->w, img->h);
+ if (!*alloc)
+ return NULL;
+ }
+
+ mp_image_copy_attributes(*alloc, img);
+ return *alloc;
+}
+
// Scale from src to dst - if src/dst have different parameters from previous
// calls, the context is reinitialized. Return error code. (It can fail if
// reinitialization was necessary, and swscale returned an error.)
@@ -345,8 +385,22 @@ int mp_sws_scale(struct mp_sws_context *ctx, struct mp_image *dst,
return mp_zimg_convert(ctx->zimg, dst, src) ? 0 : -1;
#endif
- sws_scale(ctx->sws, (const uint8_t *const *) src->planes, src->stride,
- 0, src->h, dst->planes, dst->stride);
+ struct mp_image *a_src = check_alignment(ctx->log, &ctx->aligned_src, src);
+ struct mp_image *a_dst = check_alignment(ctx->log, &ctx->aligned_dst, dst);
+ if (!a_src || !a_dst) {
+ MP_ERR(ctx, "image allocation failed.\n");
+ return -1;
+ }
+
+ if (a_src != src)
+ mp_image_copy(a_src, src);
+
+ sws_scale(ctx->sws, (const uint8_t *const *) a_src->planes, a_src->stride,
+ 0, a_src->h, a_dst->planes, a_dst->stride);
+
+ if (a_dst != dst)
+ mp_image_copy(dst, a_dst);
+
return 0;
}
diff --git a/video/sws_utils.h b/video/sws_utils.h
index 9e7f24e0a7..24bec078ae 100644
--- a/video/sws_utils.h
+++ b/video/sws_utils.h
@@ -63,6 +63,7 @@ struct mp_sws_context {
struct mp_sws_context *cached; // contains parameters for which sws is valid
struct mp_zimg_context *zimg;
bool zimg_ok;
+ struct mp_image *aligned_src, *aligned_dst;
};
struct mp_sws_context *mp_sws_alloc(void *talloc_ctx);