This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 415c9f7b35e19a0136517c80c29e33e7bfc8d52e Author: Kacper Michajłow <[email protected]> AuthorDate: Sun Sep 13 03:30:17 2026 +0200 Commit: Kacper Michajłow <[email protected]> CommitDate: Thu Sep 24 18:08:16 2026 +0000 avutil/mem: give av_malloc exact redzones under ASan on Windows The LLVM ASan runtime for Windows has no interceptor for the _aligned_malloc family. The UCRT builds it on top of the intercepted malloc, so blocks are tracked and use after free is caught, but the alignment slack on both sides of the returned pointer stays addressable and small overflows and underflows go unnoticed. The MSVC runtime intercepts the family and does not have this problem. This probably could be fixed in compiler-rt upstream, but it's not much code to support it, so let's have it. Which runtime is linked cannot be told at compile time. The Visual Studio one intercepts the family, clang from LLVM or MSYS2 links one that does not, so probe once whether the byte after an aligned allocation is poisoned and align by hand only when it is not. Tested with the MSYS2 clang64 LLVM 22 runtime, which needs it, and the Visual Studio 2026 MSVC 14.51 runtime, which does not. LLVM adds the interceptors in llvm/llvm-project#82049, still open. Signed-off-by: Kacper Michajłow <[email protected]> --- libavutil/mem.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 3 deletions(-) diff --git a/libavutil/mem.c b/libavutil/mem.c index a796d4dde1..5c3a2ffeb4 100644 --- a/libavutil/mem.c +++ b/libavutil/mem.c @@ -75,6 +75,92 @@ static void poison_memory(void *ptr, size_t size) FF_MEM_UNDEFINED(ptr, size); } +/* The LLVM ASan runtime for Windows does not intercept the _aligned_malloc + * family, so the alignment slack around a block stays addressable and small + * overflows go unnoticed, while the runtime shipped with Visual Studio does + * intercept it. Which one is linked cannot be told at compile time, so probe + * once whether the byte after an aligned allocation is poisoned and align by + * hand only when it is not. */ +#if HAVE_ASAN && HAVE_ALIGNED_MALLOC && !HAVE_POSIX_MEMALIGN && !HAVE_MEMALIGN +#define ASAN_ALIGNED_ALLOC 1 + +static int asan_aligned_alloc_needed(void) +{ + static atomic_int needed = -1; + int ret = atomic_load_explicit(&needed, memory_order_relaxed); + + if (ret < 0) { + uint8_t *p = _aligned_malloc(16, ALIGN); + int probed = !p || !__asan_address_is_poisoned(p + 16); + _aligned_free(p); + /* Every block must be freed by the allocator that made it, so the + * first probe to finish decides for all callers. */ + if (atomic_compare_exchange_strong_explicit(&needed, &ret, probed, + memory_order_relaxed, + memory_order_relaxed)) + ret = probed; + } + return ret; +} + +typedef struct AsanAlignedHeader { + void *base; + size_t size; +} AsanAlignedHeader; + +static void *asan_aligned_malloc(size_t size) +{ + AsanAlignedHeader *hdr; + uint8_t *base, *ptr; + size_t total; + + if (size > SIZE_MAX - ALIGN - sizeof(*hdr)) + return NULL; + total = size + ALIGN + sizeof(*hdr); + base = malloc(total); + if (!base) + return NULL; + ptr = (uint8_t *)FFALIGN((uintptr_t)base + sizeof(*hdr), ALIGN); + hdr = (AsanAlignedHeader *)ptr - 1; + hdr->base = base; + hdr->size = size; + FF_ASAN_POISON(base, ptr - base); + FF_ASAN_POISON(ptr + size, base + total - (ptr + size)); + return ptr; +} + +static AsanAlignedHeader *asan_aligned_header(void *ptr) +{ + AsanAlignedHeader *hdr = (AsanAlignedHeader *)ptr - 1; + FF_ASAN_UNPOISON(hdr, sizeof(*hdr)); + return hdr; +} + +static void asan_aligned_free(void *ptr) +{ + if (ptr) + free(asan_aligned_header(ptr)->base); +} + +static void *asan_aligned_realloc(void *ptr, size_t size) +{ + AsanAlignedHeader *hdr; + void *ret; + + if (!ptr) + return asan_aligned_malloc(size); + ret = asan_aligned_malloc(size); + if (!ret) + return NULL; + hdr = asan_aligned_header(ptr); + memcpy(ret, ptr, FFMIN(size, hdr->size)); + free(hdr->base); + return ret; +} +#else +#define ASAN_ALIGNED_ALLOC 0 +#endif + /* NOTE: if you want to override these functions with your own * implementations (not recommended) you have to link libav* as * dynamic libraries and remove -Wl,-Bsymbolic from the linker flags. @@ -111,7 +197,12 @@ void *av_malloc(size_t size) if (size > atomic_load_explicit(&max_alloc_size, memory_order_relaxed)) return NULL; -#if HAVE_POSIX_MEMALIGN +#if ASAN_ALIGNED_ALLOC + if (asan_aligned_alloc_needed()) + ptr = asan_aligned_malloc(size); + else + ptr = _aligned_malloc(size, ALIGN); +#elif HAVE_POSIX_MEMALIGN if (size) //OS X on SDK 10.6 has a broken posix_memalign implementation if (posix_memalign(&ptr, ALIGN, size)) ptr = NULL; @@ -165,7 +256,12 @@ void *av_realloc(void *ptr, size_t size) if (size > atomic_load_explicit(&max_alloc_size, memory_order_relaxed)) return NULL; -#if HAVE_ALIGNED_MALLOC +#if ASAN_ALIGNED_ALLOC + if (asan_aligned_alloc_needed()) + ret = asan_aligned_realloc(ptr, size + !size); + else + ret = _aligned_realloc(ptr, size + !size, ALIGN); +#elif HAVE_ALIGNED_MALLOC ret = _aligned_realloc(ptr, size + !size, ALIGN); #else ret = realloc(ptr, size + !size); @@ -242,7 +338,12 @@ int av_reallocp_array(void *ptr, size_t nmemb, size_t size) void av_free(void *ptr) { -#if HAVE_ALIGNED_MALLOC +#if ASAN_ALIGNED_ALLOC + if (asan_aligned_alloc_needed()) + asan_aligned_free(ptr); + else + _aligned_free(ptr); +#elif HAVE_ALIGNED_MALLOC _aligned_free(ptr); #else free(ptr); -- To stop receiving notification emails like this one, please contact [email protected]. _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
