PR #25298 opened by Marti2203
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25298
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25298.patch
`parse_psfile()` reads the Photoshop curves file through `av_file_map()` and
steps through it by advancing `buf` and decreasing `size`. At the end it calls
`av_file_unmap(buf, size)` with those advanced values instead of the pointer
and size the mapping returned. With mmap, `munmap()` gets a pointer that is no
longer page aligned and fails with EINVAL, so the mapping leaks every time the
filter is initialised with `psfile` and on every `psfile` command. On builds
without mmap, `av_file_unmap()` calls `av_free()` on a pointer into the middle
of the allocation.
Reproducer (`repro.sh` in the drafts generates a 74-byte .acv file with printf):
```
strace -f -e trace=mmap,munmap ffmpeg -v error -f lavfi -i
testsrc=s=32x32:d=0.1 -vf curves=psfile=t.acv -f null -
```
Before:
```
mmap(NULL, 74, PROT_READ|PROT_WRITE, MAP_PRIVATE, 3, 0) = 0x7f784bd4b000
munmap(0x7f784bd4b03c, 14) = -1 EINVAL (Invalid argument)
```
After:
```
mmap(NULL, 74, PROT_READ|PROT_WRITE, MAP_PRIVATE, 3, 0) = 0x7fad83931000
munmap(0x7fad83931000, 74) = 0
```
To check the non-mmap path, I built with ASan and `HAVE_MMAP` set to 0 in
config.h. With that build, the same command on master aborts with:
```
ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed:
0x60e00000083c
#1 parse_psfile libavfilter/vf_curves.c:638
#2 curves_init libavfilter/vf_curves.c:723
```
With the patch it runs cleanly.
The patch keeps the mapped pointer and size in separate variables and unmaps
those. The filter output does not change (identical framecrc with the psfile
before and after), and `fate-filter-curves` passes. I did not add a FATE test:
the bug only shows up as a leak or an invalid free, which a normal FATE run
cannot see, and a test would need a binary .acv file under tests/.
This issue was found by an LLM-Enhanced Static Analysis Engine and verified
manually.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
From 107a4e9df5f8d385a13b5215b8c3ec25e0617fbf Mon Sep 17 00:00:00 2001
From: Martin Mirchev <[email protected]>
Date: Wed, 21 Oct 2026 18:24:22 +0800
Subject: [PATCH] avfilter/vf_curves: unmap the psfile with the mapped pointer
and size
parse_psfile() advances buf and decrements size while reading the
file, and then passed these to av_file_unmap() instead of what
av_file_map() returned. With mmap, munmap() then fails with EINVAL
because the pointer is no longer page aligned, so the mapping leaks on
every init and every psfile command; on builds without mmap, av_free()
is called on a pointer into the middle of the buffer.
Keep the mapped pointer and size and unmap those.
Signed-off-by: Martin Mirchev <[email protected]>
---
libavfilter/vf_curves.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/libavfilter/vf_curves.c b/libavfilter/vf_curves.c
index 4a7a51a3db6..69e9c5257c4 100644
--- a/libavfilter/vf_curves.c
+++ b/libavfilter/vf_curves.c
@@ -586,17 +586,19 @@ end:
static int parse_psfile(AVFilterContext *ctx, const char *fname)
{
CurvesContext *curves = ctx->priv;
- uint8_t *buf;
- size_t size;
+ uint8_t *map, *buf;
+ size_t map_size, size;
int i, ret, version av_unused, nb_curves;
AVBPrint ptstr;
static const int comp_ids[] = {3, 0, 1, 2};
av_bprint_init(&ptstr, 0, AV_BPRINT_SIZE_AUTOMATIC);
- ret = av_file_map(fname, &buf, &size, 0, NULL);
+ ret = av_file_map(fname, &map, &map_size, 0, NULL);
if (ret < 0)
return ret;
+ buf = map;
+ size = map_size;
#define READ16(dst) do { \
if (size < 2) { \
@@ -635,7 +637,7 @@ static int parse_psfile(AVFilterContext *ctx, const char
*fname)
}
end:
av_bprint_finalize(&ptstr, NULL);
- av_file_unmap(buf, size);
+ av_file_unmap(map, map_size);
return ret;
}
--
2.54.0
_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]