Skip to content

[runtime/onnxruntime] Memory leak: new[] without delete[] in FunTpassInferBuffer second loop #2845

@wangxiuwen

Description

@wangxiuwen

Summary

FunTpassInferBuffer in runtime/onnxruntime/src/funasrruntime.cpp allocates buff and len via new[] on every iteration of the timestamp loop but never calls delete[]. Confirmed by AddressSanitizer.

Location

runtime/onnxruntime/src/funasrruntime.cpp (current main), the second while(audio->FetchTpass(frame) > 0) loop, around lines 565-568:

while(audio->FetchTpass(frame) > 0){
    ...
    float** buff;
    int* len;
    buff = new float*[1];   // ← leaked every iteration
    len  = new int[1];      // ← leaked every iteration
    buff[0] = frame->data;
    len[0] = frame->len;
    ...
    if(frame != nullptr){
        delete frame;
        frame = nullptr;
    }
}                            // ← buff and len NEVER deleted

For comparison, FunOfflineInferBuffer in the same file (around line 277-285) correctly does delete[] buff/len/flag/start_time at the loop tail. Only the 2pass / TpassStream path is missing the cleanup.

ASAN evidence

Rebuilt the runtime with -fsanitize=address and ran 100 inference calls end-to-end, then SIGTERM. LSan reports exactly:

Direct leak of 800 byte(s) in 100 object(s) allocated from:
    #0 0x... in operator new[]
    #1 0x... in FunTpassInferBuffer ... /onnxruntime/src/funasrruntime.cpp:567
    #2 0x... in funasr_tpass_offline_infer ... /onnxruntime/src/funasr_capi.cpp:287

Direct leak of 400 byte(s) in 100 object(s) allocated from:
    #0 0x... in operator new[]
    #1 0x... in FunTpassInferBuffer ... /onnxruntime/src/funasrruntime.cpp:568

100 inferences × (1 × new float*[1] + 1 × new int[1]) = 100 × 8B + 100 × 4B = 1200B. The leak rate scales with the number of timestamp segments per inference (the FetchTpass loop iteration count), so for streams with multi-segment audio the absolute number is higher.

Proposed fix

Add delete[] buff; delete[] len; before the existing delete frame cleanup at the loop tail:

             if (!(p_result->stamp).empty()){
                 p_result->stamp_sents = funasr::TimestampSentence(p_result->tpass_msg, p_result->stamp);
             }
+            delete[] buff;
+            delete[] len;
             if(frame != nullptr){
                 delete frame;
                 frame = nullptr;
             }
         }

After applying, the same ASAN run shows zero leak from FunTpassInferBuffer.

Why this is worth fixing

Per-request 12-byte leak feels small but the runtime is typically deployed as a long-lived service. At 100 QPS sustained, this alone amounts to ~100MB/day of allocator-managed memory not counting glibc/jemalloc per-allocation header overhead, which the reported Direct leak of N byte(s) numbers do not include. Multiple historical issues (#943, #1694, #1708) report production memory growth in funasr-runtime-sdk-cpu that are likely the same root cause.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions