Component
Rust — src/rust/src/demuxer/mp4.rs (sink: mprint() in src/lib_ccx/utility.c)
Problem
processmp4_rust() builds a diagnostic string with the user-supplied input filename interpolated directly into it, then passes that string as the format-string argument to mprint():
https://github.com/CCExtractor/ccextractor/blob/master/src/rust/src/demuxer/mp4.rs#L145-L146
let open_msg = format!("Opening '{}' with FFmpeg: \0", path_display);
mprint(open_msg.as_ptr() as *const c_char);
mprint() forwards its first argument straight to vfprintf() (src/lib_ccx/utility.c:194) with no variadic arguments supplied. Since path_display is the raw input filename, any % conversion specifier in the filename (%s, %x, %n, ...) is interpreted by vfprintf against nonexistent stack arguments — undefined behavior ranging from a crash to a stack-memory read, and potentially worse depending on libc hardening.
The same unguarded format!() → mprint() pattern is repeated 3 more times in the same file:
mp4.rs:152-153 (FFmpeg-open error message)
mp4.rs:641-642 and mp4.rs:668-670 (chapters-dump path)
Reproduction (confirmed against current master, d449d48b)
Built with the (opt-in) FFmpeg MP4 demuxer enabled and ran:
$ ./ccextractor '%s%s%s%s%s%s%s%s.mp4'
...
File seems to be a MP4[KAnalyzing data with FFmpeg (MP4 demuxer)
Segmentation fault (core dumped)
GDB backtrace confirms the crash is inside vfprintf processing the filename as a format string:
#0 __strlen_avx2 ()
#1 __printf_buffer (... format="Opening '%s%s%s%s%s%s%s%s.mp4' with FFmpeg: " ...)
#2 __vfprintf_internal (... format="Opening '%s%s%s%s%s%s%s%s.mp4' with FFmpeg: " ...)
#4 vfprintf (__fmt=... "Opening '%s%s%s%s%s%s%s%s.mp4' with FFmpeg: " ...)
#5 mprint (fmt=...) at src/lib_ccx/utility.c:194
#6 ccx_rust::demuxer::mp4::processmp4_rust ()
#7 ccxr_processmp4 ()
#8 start_ccx () at src/ccextractor.c:238
The test file was a real, valid FFmpeg-generated H.264 MP4, just renamed to contain %s sequences — a filename shape a user or an automated pipeline (uploads, downloads, batch renames) could plausibly produce, and trivially craftable by anyone who controls a filename before it reaches CCExtractor.
Scope
Only reachable in builds with the enable_mp4_ffmpeg Rust feature enabled (src/rust/Cargo.toml:52, not part of default features) / CMake WITH_FFMPEG=ON — i.e. this FFmpeg-based MP4 demuxer path, not the default GPAC-based one. Default distributed builds are not affected unless built with this flag.
Not a duplicate
Closed issue #2055 was a segfault in the old GPAC-based processmp4() (fixed by PR #2057, a NULL-deref/heap-overflow fix in parse_PAT/parse_PMT — different function, different file). This report is about the new FFmpeg-based processmp4_rust, merged only in the last few days (PR #2191). No open issue currently touches this file or this bug class.
Why it matters
This is CWE-134 (uncontrolled format string) with a real, reproducible SIGSEGV on attacker/user-influenceable input (a filename). Any service or pipeline that preserves user-supplied filenames before invoking CCExtractor with the FFmpeg MP4 path enabled is crashable this way, and format-string bugs of this class are well-established to sometimes escalate beyond a crash (e.g. stack memory disclosure via %x).
Proposed fix
Replace all 4 call sites with the safe pattern already used correctly elsewhere in the codebase — pass the message as a literal format string with the data as an argument, e.g.:
mprint(c"%s".as_ptr(), open_msg.as_ptr());
or equivalent (any variant that guarantees untrusted data is never used as the fmt parameter itself).
Component
Rust —
src/rust/src/demuxer/mp4.rs(sink:mprint()insrc/lib_ccx/utility.c)Problem
processmp4_rust()builds a diagnostic string with the user-supplied input filename interpolated directly into it, then passes that string as the format-string argument tomprint():https://github.com/CCExtractor/ccextractor/blob/master/src/rust/src/demuxer/mp4.rs#L145-L146
mprint()forwards its first argument straight tovfprintf()(src/lib_ccx/utility.c:194) with no variadic arguments supplied. Sincepath_displayis the raw input filename, any%conversion specifier in the filename (%s,%x,%n, ...) is interpreted byvfprintfagainst nonexistent stack arguments — undefined behavior ranging from a crash to a stack-memory read, and potentially worse depending on libc hardening.The same unguarded
format!()→mprint()pattern is repeated 3 more times in the same file:mp4.rs:152-153(FFmpeg-open error message)mp4.rs:641-642andmp4.rs:668-670(chapters-dump path)Reproduction (confirmed against current master,
d449d48b)Built with the (opt-in) FFmpeg MP4 demuxer enabled and ran:
GDB backtrace confirms the crash is inside
vfprintfprocessing the filename as a format string:The test file was a real, valid FFmpeg-generated H.264 MP4, just renamed to contain
%ssequences — a filename shape a user or an automated pipeline (uploads, downloads, batch renames) could plausibly produce, and trivially craftable by anyone who controls a filename before it reaches CCExtractor.Scope
Only reachable in builds with the
enable_mp4_ffmpegRust feature enabled (src/rust/Cargo.toml:52, not part of default features) / CMakeWITH_FFMPEG=ON— i.e. this FFmpeg-based MP4 demuxer path, not the default GPAC-based one. Default distributed builds are not affected unless built with this flag.Not a duplicate
Closed issue #2055 was a segfault in the old GPAC-based
processmp4()(fixed by PR #2057, a NULL-deref/heap-overflow fix inparse_PAT/parse_PMT— different function, different file). This report is about the new FFmpeg-basedprocessmp4_rust, merged only in the last few days (PR #2191). No open issue currently touches this file or this bug class.Why it matters
This is CWE-134 (uncontrolled format string) with a real, reproducible SIGSEGV on attacker/user-influenceable input (a filename). Any service or pipeline that preserves user-supplied filenames before invoking CCExtractor with the FFmpeg MP4 path enabled is crashable this way, and format-string bugs of this class are well-established to sometimes escalate beyond a crash (e.g. stack memory disclosure via
%x).Proposed fix
Replace all 4 call sites with the safe pattern already used correctly elsewhere in the codebase — pass the message as a literal format string with the data as an argument, e.g.:
or equivalent (any variant that guarantees untrusted data is never used as the
fmtparameter itself).