Skip to content

Commit 564e431

Browse files
committed
fix(llama-cpp): patch mtmd video stdin double-close (heap crash)
Upstream mtmd video input (ggml-org/llama.cpp#24269) double-fcloses the ffmpeg/ffprobe stdin FILE: feed_stdin() fclose()s the FILE returned by subprocess_stdin() (which is sp->stdin_file), then subprocess_destroy() fclose()s the same pointer again -> heap corruption that aborts the backend on any base64 input_video request (the CLI --video file path is unaffected). Vendor a one-line fix (null sp->stdin_file after fclose) via prepare.sh's patches/ until upstream merges it. Verified e2e with gemma-4-e2b-it-qat-q4_0: video frames decode via ffmpeg and the model answers correctly (red clip -> 'Red', blue -> 'Blue'). Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
1 parent 23c5c3f commit 564e431

1 file changed

Lines changed: 30 additions & 0 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
From: Ettore Di Giacinto <mudler@localai.io>
2+
Subject: [PATCH] mtmd: fix double-close of ffmpeg/ffprobe stdin in video helper
3+
4+
mtmd_helper_video::feed_stdin() obtains the subprocess stdin via
5+
subprocess_stdin(sp), which returns sp->stdin_file directly, then
6+
fclose()s that FILE. Closing the local copy leaves sp->stdin_file
7+
dangling (still non-NULL), so the subsequent subprocess_destroy()
8+
fclose()s the same FILE a second time. The resulting heap corruption
9+
aborts the process ("corrupted double-linked list" / "corrupted size
10+
vs. prev_size") - notably on the server's base64 input_video path,
11+
where every probe()/start_ffmpeg() feeds the buffer via stdin. The CLI
12+
--video file path is unaffected (it never spawns the stdin feeder).
13+
14+
Clear sp->stdin_file after fclose so subprocess_destroy() skips it.
15+
16+
--- a/tools/mtmd/mtmd-helper.cpp
17+
+++ b/tools/mtmd/mtmd-helper.cpp
18+
@@ -642,7 +642,12 @@
19+
LOG_DBG("%s: feeding %zu bytes to stdin\n", __func__, input_buf.size());
20+
size_t written = fwrite(input_buf.data(), 1, input_buf.size(), f);
21+
LOG_DBG("%s: wrote %zu bytes, closing stdin\n", __func__, written);
22+
fclose(f);
23+
+ // subprocess_stdin() returns sp->stdin_file directly; fclosing our local
24+
+ // copy leaves the struct pointer dangling, so subprocess_destroy() would
25+
+ // fclose() the same FILE again -> heap corruption. Null it so the later
26+
+ // destroy skips stdin.
27+
+ sp->stdin_file = nullptr;
28+
}
29+
30+
bool probe(float fps_target_arg) {

0 commit comments

Comments
 (0)