Summary

  • CVE: CVE-2026-TBD (CVE ID requested via MITRE, batch submission 2026-07-19)
  • Component: filter.c (filter-session core / scheduler)
  • Vulnerability Type: Use-after-free (write)
  • Vendor: GPAC
  • Product: GPAC (libgpac / MP4Box / gpac)
  • Affected Versions: master branch, commit 9bfcd13401cd1e52f966d03670c433d25952472c (26.03-DEV)
  • Fix Status: Fixed upstream: commit 03e5b1c (2026-07-22); issue #3746 closed 2026-07-23
  • Severity: High (7.8 / CVSS 3.1, 8.5 / CVSS 4.0): CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
  • Credit: Salim Largo (2ourc3)

Description

A heap use-after-free write exists in the core of GPAC’s filter-session scheduler, in gf_filter_process_task(). If a filter’s own process() callback causes the filter itself to be torn down (e.g. by calling gf_filter_setup_failure() internally), the dispatcher that invoked the callback keeps writing to the now-freed GF_Filter structure once the callback returns.

This is the core-side manifestation of the leaf-filter lifetime bugs reported alongside it (ISOBMFF reader, AV1 reframer, HTTP input): all are triggered by the same underlying hazard, namely that the filter core does not re-validate a filter’s liveness after a callback that may have freed it.


Root cause

gf_filter_process_task (src/filter_core/filter.c:3257-3260):

e = filter->freg->process(filter);       // 3257: callback may destroy `filter`
gf_logs_thread_untag(filter);
filter->in_process_callback = GF_FALSE;  // 3259/3260: use-after-free write

A demuxer’s process() callback triggers teardown of its own filter (freeing the GF_Filter), but the core dispatcher keeps touching filter->... after the call returns. This is likely related to the PID-init use-after-free reported separately, which shares the same filter-graph lifetime root cause.


Proof of Concept

afl-clang-fast -O1 -g -fsanitize=address -DGPAC_HAVE_CONFIG_H -I gpac_src -I gpac_src/include \
  harnesses/session_replay.c -L gpac_src/bin/gcc -lgpac -Wl,-rpath,gpac_src/bin/gcc -o session_replay
LD_LIBRARY_PATH=gpac_src/bin/gcc ./session_replay poc_file_uaf_write_filter_process_task

Equivalent path: gf_fs_new(0, GF_FS_SCHEDULER_DIRECT, GF_FS_FLAG_NO_REGULATION) running inspect:deep:analyze=on:allp over the input registered as a gmem:// blob. The gpac / MP4Box CLI does not trigger this directly.

Observed ASan output (trimmed)

==98624==ERROR: AddressSanitizer: heap-use-after-free on address 0x7cfce9ff6444
WRITE of size 4 at 0x7cfce9ff6444 thread T0
    #0 gf_filter_process_task src/filter_core/filter.c:3260:30
    #1 gf_fs_post_task_ex src/filter_core/filter_session.c:973:3
    #2 gf_filter_post_process_task_internal src/filter_core/filter.c
    ...

0x7cfce9ff6444 is located 196 bytes inside of 936-byte region
freed by thread T0 here:
    #0 free (asan interceptor)
    #1 gf_filter_del src/filter_core/filter.c:804:2
    #2 gf_filter_setup_failure_task src/filter_core/filter.c:3668:2
    ...
    #7 txtin_process src/filters/load_text.c:4188:4
    #8 gf_filter_process_task src/filter_core/filter.c:3257:7
    ...

SUMMARY: AddressSanitizer: heap-use-after-free src/filter_core/filter.c:3260:30 in gf_filter_process_task

Fix

Fixed upstream in commit 03e5b1c (2026-07-22), titled “fuzz: fix mem errors from filter setup_failure() and others”, a single patch that closed all ten vulnerabilities reported in this batch.

This is the bug whose fix anchors the whole batch: gf_filter_setup_failure_task() (src/filter_core/filter.c) now defers a filter’s teardown instead of freeing it synchronously while a task for that same filter (such as the process() call that triggered the teardown) is still on the call stack:

/* Defer teardown if a task for this filter is currently executing on the direct-call stack.
   gf_filter_process_task clears in_process_callback after freg->process() returns;
   gf_fs_post_task_ex clears DIRECT_SCHEDULED after any direct-call task returns.
   Running the teardown synchronously here would free the filter mid-stack. */
if (f->in_process_callback || f->scheduled_for_next_task == GF_FILTER_DIRECT_SCHEDULED) {
    task->requeue_request = GF_TRUE;
    return;
}

gf_filter_process_task() can now safely write filter->in_process_callback = GF_FALSE after process() returns, because the filter is guaranteed not to have been freed mid-call; the actual teardown is deferred to a later task, after the stack has unwound. The upstream maintainer (aureliendavid) also hardened a handful of related probe functions (load_text.c, ff_dmx.c) as part of the same commit.


Impact

Use-after-free write in the core filter scheduler, reachable by any untrusted input that makes a filter self-destruct during processing (observed via the text/subtitle loader, load_text.c). Primary impact is denial of service (crash); heap corruption on the freed 936-byte region raises the possibility of further exploitation.


Timeline

DateAction
2026-07-19Reported upstream (issue #3746)
2026-07-21Session-replay harness shared with maintainer on request
2026-07-22Fixed upstream, commit 03e5b1c
2026-07-23Issue closed

References