Skip to content

[FIX] Prevent MSVC cross-CRT invalid free on output_filename#2147

Open
Atul-Chahar wants to merge 2 commits intoCCExtractor:masterfrom
Atul-Chahar:fix/debug-build-double-free
Open

[FIX] Prevent MSVC cross-CRT invalid free on output_filename#2147
Atul-Chahar wants to merge 2 commits intoCCExtractor:masterfrom
Atul-Chahar:fix/debug-build-double-free

Conversation

@Atul-Chahar
Copy link
Contributor

@Atul-Chahar Atul-Chahar commented Feb 26, 2026

In raising this pull request, I confirm the following (please check boxes):

  • I have read and understood the contributors guide.
  • I have checked that another pull request for this purpose does not exist.
  • I have considered, and confirmed that this submission will be valuable to others.
  • I accept that this submission may not be used, and the pull request closed at the will of the maintainer.
  • I give this submission freely, and claim no ownership to its content.
  • I have mentioned this change in the changelog.

My familiarity with the project is as follows (check one):

  • I have never used CCExtractor.
  • I have used CCExtractor just a couple of times.
  • I absolutely love CCExtractor, but have not contributed previously.
  • I am an active contributor to CCExtractor.

Summary

Resolves the Windows MSVC Debug build crash reported in #2126. The root cause was a cross-CRT boundary bug: ccx_options.enc_cfg.output_filename is allocated by Rust (CString::into_raw), but C code was calling freep() on it — triggering the MSVC Debug allocator's invalid-free assertion.

Fix

update_encoder_list_cinfo() and segment_output_file() now work with a local copy (struct encoder_cfg local_cfg) for dynamic multi-program file allocations, keeping the global Rust-allocated pointer untouched. The crash-inducing freep() call in dinit_libraries() was also removed.

Changes

  • src/lib_ccx/general_loop.c — use local encoder_cfg copy for multi-program output
  • src/lib_ccx/lib_ccx.c — remove invalid freep() on Rust-allocated string, fix cleanup path
  • docs/CHANGES.TXT — added changelog entry

CI Notes

The 9 Linux sample platform failures (--autoprogram, --out=spupng, --startcredits tests) are pre-existing on upstream master and unrelated to this PR. The Windows sample platform confirms all 237/237 tests pass and this PR actually fixes those same 9 tests on Windows.

Fixes #2126

Copy link
Contributor

@cfsmp3 cfsmp3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues:

  1. Unrelated SCC fps change: The SCC framerate fix is a duplicate of #2146. Please remove it from this PR.

  2. Double free claim not substantiated: I ran valgrind on master with --multiprogram and there is no "Invalid free" reported. Code analysis shows init_encoder always calls strdup on the filename, so freep in the cleanup path is correct — it frees the strdup'd copy, not the original. Removing the freep actually introduces a small memory leak (the strdup'd 2 bytes are never freed).

Please provide concrete evidence of the double free (e.g. MSVC Debug output, valgrind trace, or a specific code path that skips the strdup). Without that, this change is incorrect.

@Atul-Chahar Atul-Chahar force-pushed the fix/debug-build-double-free branch from 43998f4 to 7b788d2 Compare March 1, 2026 19:22
@Atul-Chahar
Copy link
Contributor Author

Atul-Chahar commented Mar 1, 2026

@cfsmp3 Thanks for the thorough review! I've updated the PR with an atomic fix addressing both of your points:

  1. Unrelated SCC fps change: I have completely removed the SCC framerate changes as requested. They are now managed exclusively in [FIX] Use dynamic current_fps instead of hardcoded 29.97 in SCC timecodes #2146.
  2. Double free claim not substantiated: You are entirely correct that init_encoder uses strdup, effectively isolating the memory and wb->filename. The true underlying mechanism for the "Invalid Free" assertions in the Windows Debug CRT actually stems from cross-boundary allocations between Rust and C.

Specifically, ccx_options.enc_cfg.output_filename is initialized inside Rust via CString::into_raw. When update_encoder_list_cinfo() or dinit_libraries() (at the end of the run) blindly called freep() on this exact Rust-allocated pointer, the Windows MSVC allocator caught the mismatch between headers and crashed. The previous attempt to remove the freep() inadvertently allowed a malloc()'d string to mask the Rust pointer, stopping the crash but inherently introducing the memory leak you identified.

The Correct Atomic Fix:
Instead of modifying and mutating the global ccx_options.enc_cfg strings, update_encoder_list_cinfo and segment_output_file now allocate dynamic multi-program strings via a strictly local copy (struct encoder_cfg local_cfg = ccx_options.enc_cfg;). This completely avoids clobbering the Rust string or calling C's free() on Rust strings, and allows malloc()'d strings to be cleanly freep()'d locally without leaks. I've also removed the crash-inducing freep() originally placed inside dinit_libraries.

Let me know what you think of this approach!

@Atul-Chahar Atul-Chahar changed the title fix: remove double free in multiprogram encoder initialization (Debug build) fix: prevent MSVC cross-CRT invalid free on output_filename Mar 1, 2026
@Atul-Chahar Atul-Chahar requested a review from cfsmp3 March 2, 2026 04:26
…er_cfg

- Removed premature freep on `ccx_options.enc_cfg.output_filename` since
  it is allocated by Rust via `CString::into_raw`, which causes CRT assertion
  failures on Windows Debug builds when freed by C's `free()`.
- Used a local copy of `struct encoder_cfg` for dynamically generated
  filenames in `update_encoder_list_cinfo` and `segment_output_file` to avoid
  overwriting the Rust string pointer with a `malloc`'d string, successfully
  preventing memory leaks.
@Atul-Chahar Atul-Chahar changed the title fix: prevent MSVC cross-CRT invalid free on output_filename [FIX] Prevent MSVC cross-CRT invalid free on output_filename Mar 3, 2026
@Atul-Chahar Atul-Chahar force-pushed the fix/debug-build-double-free branch from 7b788d2 to f3848e0 Compare March 3, 2026 19:33
@ccextractor-bot
Copy link
Collaborator

CCExtractor CI platform finished running the test files on linux. Below is a summary of the test results, when compared to test for commit f377be9...:
Report Name Tests Passed
Broken 13/13
CEA-708 14/14
DVB 7/7
DVD 3/3
DVR-MS 2/2
General 27/27
Hardsubx 1/1
Hauppage 3/3
MP4 3/3
NoCC 10/10
Options 86/86
Teletext 21/21
WTV 13/13
XDS 34/34

Congratulations: Merging this PR would fix the following tests:

  • ccextractor --autoprogram --out=srt --latin1 --quant 0 85271be4d2..., Last passed: Never
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla dab1c1bd65..., Last passed: Never
  • ccextractor --out=srt --latin1 --autoprogram 29e5ffd34b..., Last passed: Never
  • ccextractor --out=spupng c83f765c66..., Last passed: Never
  • ccextractor --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9..., Last passed: Never
  • ccextractor --startcreditsnotbefore 1 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9..., Last passed: Never
  • ccextractor --startcreditsnotafter 2 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9..., Last passed: Never
  • ccextractor --startcreditsforatleast 1 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9..., Last passed: Never
  • ccextractor --startcreditsforatmost 2 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9..., Last passed: Never

All tests passed completely.

Check the result page for more info.

@ccextractor-bot
Copy link
Collaborator

CCExtractor CI platform finished running the test files on windows. Below is a summary of the test results, when compared to test for commit f377be9...:
Report Name Tests Passed
Broken 13/13
CEA-708 14/14
DVB 6/7
DVD 3/3
DVR-MS 2/2
General 25/27
Hardsubx 1/1
Hauppage 3/3
MP4 3/3
NoCC 10/10
Options 80/86
Teletext 21/21
WTV 13/13
XDS 34/34

Your PR breaks these cases:

  • ccextractor --autoprogram --out=srt --latin1 --quant 0 85271be4d2...
  • ccextractor --autoprogram --out=ttxt --latin1 --ucla dab1c1bd65...
  • ccextractor --out=srt --latin1 --autoprogram 29e5ffd34b...
  • ccextractor --out=spupng c83f765c66...
  • ccextractor --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9...
  • ccextractor --startcreditsnotbefore 1 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9...
  • ccextractor --startcreditsnotafter 2 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9...
  • ccextractor --startcreditsforatleast 1 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9...
  • ccextractor --startcreditsforatmost 2 --startcreditstext "CCextractor Start crdit Testing" c4dd893cb9...

It seems that not all tests were passed completely. This is an indication that the output of some files is not as expected (but might be according to you).

Check the result page for more info.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] CCExtractor 0.96 x64 Debug build displays error message at end of runs on Windows 11 PC

3 participants