Skip to content

An LTX-2.5 render leaks DevicePool blocks from the Gemma-4 rope cache, and the lane that would report it keeps getting cancelled #1037

Description

@localai-bot

Found while closing a sanitizer gap on main: PR #1036 merged with no completed x86 build-test and no sanitizer verdict because two ci runs fired on the same SHA and the second cancelled the first, then skipped its own build jobs. I ran the lane locally afterwards. The leak it found is not #1036's — see the control below.

The finding

test_ltx2_video under CI's own VLLM_CPP_SANITIZE=address,undefined lane, at main d1b0ea3a8:

[doctest] test cases: 57 | 57 passed | 0 failed | 0 skipped
EXIT=1
==714711==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 65536 byte(s) in 1 object(s) allocated from:
    #0  aligned_alloc
    #1  AllocAligned64            src/vt/cpu/cpu_backend.cpp:20
    #2  Alloc                     src/vt/cpu/cpu_backend.cpp:42
    #3  vllm::DevicePool::Get     include/vllm/model_executor/models/device_pool.h
    #4  vllm::dense_attn::DBuf::DBuf
    #5  BuildProportionalRopeCache  src/vllm/model_executor/models/gemma4.cpp:192
    #6  ForwardBody                 src/vllm/model_executor/models/gemma4.cpp:438
    #7  Gemma4Model::ForwardHiddenStates
    #8  vllm::Ltx2EncodePromptToConditioning
    #9  Ltx2VideoEngine::Generate
   #10  RenderPrompt              tests/vllm/multimodal/test_ltx2_video.cpp:3493

50 direct leaks, 91456 bytes total. The path is the text encoder inside an ordinary LTX-2.5 render, not a test fixture.

Note the shape: the doctest summary reads 57 passed | 0 failed and the process exits 1. A reader grepping assertions: or failed sees a clean run. Only the exit code carries the verdict — the same trap this repo has now hit for thrown cases, non-building mutations, and cancelled CI.

Pre-existing, proven rather than argued

Same lane, same configuration, on the parent commit 6621576ac:

PARENT test_ltx2_video EXIT=1
leak reports on PARENT: 1
Direct leak of 65536 byte(s) in 1 object(s)   <- identical
Direct leak of 3200  byte(s) in 2 object(s)   <- identical
Direct leak of 2560  byte(s) in 1 object(s)   <- identical

Identical leaks on both sides. #1036 touched ltx2_video_vae.cpp and test_ltx2_vae.cpp only — never gemma4.cpp or device_pool.h. It is exonerated by measurement.

Why nobody has seen it

CI does run ASAN_OPTIONS: detect_leaks=1 (.github/workflows/ci.yml:1045), so this is a lane that would report it. Of the last 12 main runs: 6 success, 4 cancelled, 1 failure. And on #1036 specifically the lane never ran at all — cancelled at 31% Building with zero tests executed, while gh pr checks rendered that as a failure.

So the detector works and its output is being lost. That is the same mechanism that let #904's use-after-free sit on main for five hours today: the lane was dismissed as known-red rather than read.

What is actually owed

Someone who knows the pool's intent should decide whether this is a leak or a deliberate never-freed cache. DevicePool is a free-list allocator; a pool that retains blocks for reuse and never returns them at exit is a legitimate design that LeakSanitizer will always flag. If that is the intent, the fix is a suppression with a stated reason — not silence, and not detect_leaks=0, which would blind the lane to real leaks.

If it is not intended, then an LTX-2.5 render leaks 91456 bytes per invocation on the text-encoder path.

Either way the current state is the worst one: a real detector firing into a lane whose output is routinely discarded.

Related: #904 (the use-after-free the same lane caught), #949 (a defect class visible only to continue-on-error lanes), #584.

FOLLOWING_AGENTS_PROTOCOL

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions