Skip to content

fix(memory): close 4 feature-extractor init() leak clusters (CERT MEM31-C, ADR-0906) - #395

Closed
lusoris wants to merge 0 commit into
masterfrom
fix/memory-allocator-audit
Closed

lusoris wants to merge 0 commit into
masterfrom
fix/memory-allocator-audit

Conversation

@lusoris

@lusoris lusoris commented May 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Audit of fork-added CPU feature-extractor init() paths against CERT MEM31-C. Framework-level invariant rediscovered: vmaf_feature_extractor_context_destroy() frees the priv struct itself but NEVER invokes the extractor's close() callback when init() returned non-zero (the is_initialized gate stays false). Four leak clusters fixed; ASan + leak-detect clean across fast suite (49/49) and full suite (63/63) + e2e cambi/ssimulacra2/motion runs. Zero golden-data drift (CPU path is allocator-only, no math touched).

Type

  • fix — bug fix

Per-site root cause

Site Class Buffers leaked Fix
core/src/feature/ssimulacra2.c::init OOM unwind 12 × aligned_malloc (ref_lin, dist_lin, ref_xyb, dist_xyb, mu1, mu2, sigma1_sq, sigma2_sq, sigma12, mul_buf, scratch, col_state) Inline aligned_free() sequence + memset(s, 0, sizeof(*s)) before return -ENOMEM
core/src/feature/cambi.c::init OOM + EINVAL unwind up to 9 × aligned_malloc + feature_name_dict + up to 3 × heatmap FILE* Extracted cambi_init_release_partials() mirroring close_cambi(); converted every error return to err = ...; goto fail
core/src/feature/cambi.c::set_contrast_arrays Mid-helper OOM 1–2 × aligned_malloc + previously-uncaught return code Free preceding allocations, NULL out-parameter slots; caller now checks the return code
core/src/feature/integer_motion_v2.c::init OOM unwind feature_name_dict Release dict if subsequent s->y_row allocation fails

Checklist

  • Commits follow Conventional Commits.
  • clang-format + pre-commit run --files <touched> green locally.
  • Unit tests pass under ASan: meson test -C build-mem --suite=fast (49/49 OK) and full suite (63/63 OK).
  • No SIMD/GPU code paths touched — CPU allocator-only refactor.
  • No new .c / .h files added.
  • Not a breaking change.

Bug-status hygiene (ADR-0165)

  • docs/state.md updated with T-MEMORY-ALLOCATOR-AUDIT-INIT-OOM-2026-05-30 row at the top.

Netflix golden-data gate (ADR-0024)

  • I did NOT modify any assertAlmostEqual(...) score in the Netflix golden Python tests.
  • The fix is CPU-allocator-only — VMAF scores are mathematically unaffected.

Cross-backend numerical results

N/A — CPU allocator-only refactor. No math touched. Golden CPU values unchanged.

Deep-dive deliverables (ADR-0108)

  • Research digest — docs/research/memory-allocator-audit-2026-05-30.md walks the 355-site enumeration, the audited clusters, the framework invariant, and the per-cluster verdict.
  • Decision matrix — docs/adr/0906-memory-allocator-audit-cleanup.md ## Alternatives considered lists the four candidate fix strategies (inline helper + goto fail / framework-level close-on-fail / caller-allocated priv / no fix) with pros, cons, and rejection rationale.
  • AGENTS.md invariant note — core/src/feature/AGENTS.md ## Rebase-sensitive invariants now documents the framework-level rule that init() must drain partials on every error return; references transnet_v2.c::release_buffers, feature_lpips.c::lpips_release, and the new cambi.c::cambi_init_release_partials as templates.
  • Reproducer / smoke-test command — see below.
  • CHANGELOG fragment — changelog.d/fixed/memory-allocator-audit.md.
  • Rebase note — entry added to docs/rebase-notes.md under "Memory allocator audit — init() OOM-cleanup (2026-05-30, ADR-0906)" describing the medium rebase impact on cambi.c (upstream port that grows the buffer list must extend the cleanup helper in lock-step) and zero impact on ssimulacra2.c (fork-local).

Reproducer

# 1. Build with ASan
meson setup build-mem core -Denable_cuda=false -Denable_sycl=false -Db_sanitize=address
ninja -C build-mem

# 2. Fast suite under ASan
meson test -C build-mem --suite=fast  # 49/49 OK

# 3. Full suite under ASan
meson test -C build-mem               # 63/63 OK

# 4. End-to-end cambi run with leak detection
ASAN_OPTIONS=detect_leaks=1:abort_on_error=0 \
  ./build-mem/tools/vmaf \
  -r python/test/resource/yuv/checkerboard_1920_1080_10_3_0_0.yuv \
  -d python/test/resource/yuv/checkerboard_1920_1080_10_3_1_0.yuv \
  --width 1920 --height 1080 --pixel_format 420 --bitdepth 8 \
  --feature cambi --no_prediction

# Pre-fix: leaks ~17 KB on every shrinking-memory init.
# Post-fix: exits 0, no leak report.

Test plan

  • meson test -C build-mem --suite=fast — 49/49 OK under ASan
  • meson test -C build-mem — 63/63 OK under ASan
  • End-to-end ASan smoke over cambi, ssimulacra2, motion extractors — no leak report, EXIT 0
  • pre-commit run --files <touched> — all gates green
  • bash scripts/ci/assertion-density.sh ... — PASS
  • bash scripts/ci/check-copyright.sh — PASS

Avoided overlap with

PR #297 (init_blur_array), PR #289 (CUDA PTX unload), PR #290 (HIP ssimulacra2), PR #293 (SYCL init), PR #296 (feature_extractor leaks), PR #317 (gpu_picture_pool UAF), PR #352 (sanitizer pass). All cited clusters in this PR are distinct sites.

🤖 Generated with Claude Code

@lusoris
lusoris marked this pull request as ready for review May 30, 2026 23:48
@lusoris
lusoris enabled auto-merge (squash) May 30, 2026 23:48
@lusoris
lusoris marked this pull request as draft May 31, 2026 00:52
auto-merge was automatically disabled May 31, 2026 00:52

Pull request was converted to draft

@lusoris
lusoris marked this pull request as ready for review May 31, 2026 01:46
@lusoris
lusoris enabled auto-merge (squash) May 31, 2026 01:46
@lusoris lusoris closed this May 31, 2026
auto-merge was automatically disabled May 31, 2026 01:46

Pull request was closed

@lusoris
lusoris force-pushed the fix/memory-allocator-audit branch from 3ac60d4 to e81b133 Compare May 31, 2026 01:46
@lusoris
lusoris deleted the fix/memory-allocator-audit branch June 4, 2026 10:25
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.

1 participant