Skip to content

Conversation

@THE-Amrit-mahto-05
Copy link
Contributor

@THE-Amrit-mahto-05 THE-Amrit-mahto-05 commented Jan 7, 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.

Description

fixes a critical memory leak in CCExtractor where the file buffer allocated for each demuxer instance is never freed, causing 16MB of memory to leak per instance. This occurs because Rust allocated the buffer and then "forgot" it so C code could use it, leaving it unmanaged.

Changes made

  • In ccxr_demuxer_close, the raw file buffer pointer is reclaimed as a boxed slice and dropped to allow Rust to free the memory.
  • The pointer is then set to NULL to prevent double-free issues if C code tries to clean it up.
  • Updated comments to explain why this step is necessary.

Fixes: #1993

Verification

  • Verified with multiple demuxer instances that memory usage no longer grows by 16MB per instance.
  • Ensured normal demuxer functionality is unaffected.

@THE-Amrit-mahto-05
Copy link
Contributor Author

@cfsmp3 pls review this too

@cfsmp3
Copy link
Contributor

cfsmp3 commented Jan 10, 2026

@cfsmp3 pls review this too

There's no need to tag me in every PR, I review them when I can... tagging me just adds noise, no need.

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.

Review: Changes Requested

Thanks for identifying this memory leak! Your analysis is correct - the filebuffer is allocated with Rust's Box but never freed, causing a 16MB leak per demuxer instance.

However, the current fix is incomplete and leaves a path where undefined behavior can occur.

The Problem

There are two cleanup paths in the codebase:

  1. Normal path: ccxr_demuxer_close() → later ccx_demuxer_delete()
  2. Error/early exit path: ccx_demuxer_delete() directly (without close being called)

Your PR fixes path 1, but path 2 still has a critical bug.

In ccx_demuxer.c:325, the ccx_demuxer_delete() function does:

freep(&lctx->filebuffer);

This calls C's free() on memory that was allocated by Rust's Box. This is undefined behavior - you cannot free Rust-allocated memory with C's free() because they may use different allocators.

Current State With Your PR

Cleanup Path What Happens Result
Normal (close → delete) Rust frees in close, C sees NULL in delete ✓ Works
Error (delete without close) C tries to free() Rust memory ❌ Undefined behavior

The Complete Solution

You need to ensure Rust-allocated memory is always freed by Rust, in all code paths.

Step 1: Create ccxr_demuxer_delete in Rust

Add this to src/rust/src/libccxr_exports/demuxer.rs:

/// # Safety
/// This function must be called before the C code frees the demuxer struct.
/// It frees Rust-allocated resources that C cannot safely free.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ccxr_demuxer_delete(ctx: *mut ccx_demuxer) {
    if ctx.is_null() {
        return;
    }
    let c = &mut *ctx;
    
    // Free Rust-allocated filebuffer
    if \!c.filebuffer.is_null() {
        use crate::file_functions::file::FILEBUFFERSIZE;
        let _ = Box::from_raw(std::ptr::slice_from_raw_parts_mut(
            c.filebuffer,
            FILEBUFFERSIZE,
        ));
        c.filebuffer = std::ptr::null_mut();
    }
}

Step 2: Modify ccx_demuxer_delete in C

In src/lib_ccx/ccx_demuxer.c, add the declaration and modify ccx_demuxer_delete:

// Add declaration near top of file (around line 8)
void ccxr_demuxer_delete(struct ccx_demuxer *ctx);

// Modify ccx_demuxer_delete function
void ccx_demuxer_delete(struct ccx_demuxer **ctx)
{
    struct ccx_demuxer *lctx = *ctx;
    int i;

#ifndef DISABLE_RUST
    // Let Rust free any memory it allocated
    ccxr_demuxer_delete(lctx);
#endif

    dinit_cap(lctx);
    freep(&lctx->last_pat_payload);
    
    // ... existing cleanup code ...
    
#ifdef DISABLE_RUST
    // Only free filebuffer in pure C mode - Rust handles its own memory
    freep(&lctx->filebuffer);
#else
    // Rust already freed this in ccxr_demuxer_delete, just clear the pointer
    lctx->filebuffer = NULL;
#endif

    freep(ctx);
}

Step 3: Remove filebuffer cleanup from ccxr_demuxer_close

The filebuffer should be freed in ccxr_demuxer_delete, not in ccxr_demuxer_close. The close function should only close the file descriptor. Remove the filebuffer cleanup code you added to ccxr_demuxer_close.

Why This Matters

The delete function is called during program cleanup. If there's an error or early exit, close might not be called, but delete will be. By putting the Rust memory cleanup in ccxr_demuxer_delete, we ensure:

  1. All code paths are safe - whether close is called or not
  2. No allocator mismatch - Rust always frees Rust-allocated memory
  3. No double-free - the pointer is set to NULL after freeing
  4. Backwards compatible - pure C mode (DISABLE_RUST) still works

Summary

The fix needs to be in ccxr_demuxer_delete, not ccxr_demuxer_close, because delete is the guaranteed cleanup path that always runs.

@THE-Amrit-mahto-05
Copy link
Contributor Author

THE-Amrit-mahto-05 commented Jan 14, 2026

@cfsmp3
Sorry for the delay in raising this PR. I spent a bit of extra time understanding how Rust memory management works and how it interacts with C, as well as getting familiar with the CI/testing workflow.

The PR now makes sure that Rust-allocated memory is properly freed in ccxr_demuxer_delete for all code paths, which prevents memory leaks and avoids undefined behavior when Rust and C interact as you suggested

@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 2028754...:
Report Name Tests Passed
Broken 13/13
CEA-708 14/14
DVB 7/7
DVD 3/3
DVR-MS 2/2
General 25/27
Hardsubx 1/1
Hauppage 3/3
MP4 3/3
NoCC 10/10
Options 81/86
Teletext 21/21
WTV 13/13
XDS 34/34

Your PR breaks these cases:

  • ccextractor --autoprogram --out=ttxt --latin1 --ucla dab1c1bd65...
  • ccextractor --out=srt --latin1 --autoprogram 29e5ffd34b...
  • 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...

Congratulations: Merging this PR would fix the following tests:

  • ccextractor --autoprogram --out=srt --latin1 --quant 0 85271be4d2..., Last passed: Never
  • ccextractor --out=spupng c83f765c66..., Last passed: Never

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.

@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 2028754...:
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 81/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 --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...

Congratulations: Merging this PR would fix the following tests:


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.

Memory Leak: File buffer not freed, leaking 16MB per demuxer instance

3 participants