<div dir="ltr">Hello,<br><br>I would like to report a memory leak in the client-side SDP parsing code of liveMedia. When an SDP description contains more than one "a=range: clock=<start>-<end>" attribute at the same level (session level, or within a single media/subsession), each additional occurrence leaks the previously-allocated fAbsStartTime / fAbsEndTime string buffers. It is reachable purely during SDP parsing, without any socket setup. This is a memory leak / potential resource-exhaustion issue only; I am not claiming any memory corruption or remote code execution.<br><br>I found this while evaluating an AI-assisted fuzzing harness for LIVE555 SDP parsing.<br><br>Version<br><br>Verified against live.2026.06.24, which appears to be the latest available release.<br><br>I confirmed the affected source is identical to the released tarball: the liveMedia/MediaSession.cpp I tested has md5 9933463224aa6e946112d327c7b68086, byte-for-byte identical to the MediaSession.cpp in the live.2026.06.24 release tarball. The live555-latest.tar.gz URL on <a href="http://live555.com">live555.com</a> was returning HTTP 404 at the time, so I obtained the same-versioned tarball via the VideoLAN mirror.<br><br>Build used for testing: clang++ 14 with -fsanitize=address,undefined,fuzzer, LeakSanitizer enabled.<br><br>Affected functions<br><br>Both of these overwrite the member pointers fAbsStartTime / fAbsEndTime via the shared helper parseRangeAttribute() without freeing the previous values:<br><br>  - MediaSession::parseSDPAttribute_range() (session level)<br>  - MediaSubsession::parseSDPAttribute_range() (media / subsession level)<br><br>Root cause<br><br>In parseRangeAttribute(char const*, char*& absStartTime, char*& absEndTime), on a successful clock-format parse the freshly allocated buffers are assigned directly to the reference parameters (which alias the members fAbsStartTime / fAbsEndTime):<br><br>    if (sscanfResult == 2) {<br>      absStartTime = as;   // previous value not freed<br>      absEndTime = ae;     // previous value not freed<br>    } else if (sscanfResult == 1) {<br>      absStartTime = as;   // previous value not freed<br>      delete[] ae;<br>    } ...<br><br>The members start as NULL in the constructor, and the destructor only delete[]s their final value. So the first "a=range: clock=" stores buffers with no leak, but each subsequent one overwrites the member pointers without releasing the earlier buffers, orphaning them. A single clock range does not leak, consistent with the observed behavior.<br><br>Reproduction<br><br>Both cases were reproduced deterministically with an instrumented build.<br><br>(a) Session level<br><br>SDP:<br><br>    v=0<br>    o=- 0 0 IN IP4 127.0.0.1<br>    s=R<br>    t=0 0<br>    c=IN IP4 0.0.0.0<br>    a=range:clock=20260101T000000Z-20260101T000001Z<br>    a=range:clock=20260102T000000Z-20260102T000001Z<br>    m=video 0 RTP/AVP 96<br>    a=rtpmap:96 H264/90000<br>    a=control:track1<br><br>Result: LeakSanitizer reports 2 allocations leaked, exit code 77.<br><br>(b) Media / subsession level<br><br>SDP:<br><br>    v=0<br>    o=- 0 0 IN IP4 127.0.0.1<br>    s=R<br>    t=0 0<br>    c=IN IP4 0.0.0.0<br>    m=video 0 RTP/AVP 96<br>    a=rtpmap:96 H264/90000<br>    a=control:track1<br>    a=range:clock=20260101T000000Z-20260101T000001Z<br>    a=range:clock=20260102T000000Z-20260102T000001Z<br><br>Result: LeakSanitizer reports 2 allocations leaked, 194 bytes total, exit code 77.<br><br>In both cases the SDP is passed to MediaSession::createNew(), and the session is properly closed with Medium::close() afterwards, so the leak is not a harness artifact; the orphaned buffers are unreachable by the time the destructor runs.<br><br>LeakSanitizer stack traces (condensed)<br><br>Session level:<br><br>    Direct leak allocated from:<br>      operator new[]<br>      parseRangeAttribute(...)                        MediaSession.cpp:389/390<br>      MediaSession::parseSDPAttribute_range(...)      MediaSession.cpp:421<br>      MediaSession::initializeWithSDP(...)            MediaSession.cpp:122<br>      MediaSession::createNew(...)                    MediaSession.cpp:34<br><br>Subsession level:<br><br>    Direct leak allocated from:<br>      operator new[]<br>      parseRangeAttribute(...)                        MediaSession.cpp:389/390<br>      MediaSubsession::parseSDPAttribute_range(...)   MediaSession.cpp:1142<br>      MediaSession::initializeWithSDP(...)            MediaSession.cpp:221<br>      MediaSession::createNew(...)                    MediaSession.cpp:34<br><br>Suggested fix<br><br>Since both call sites overwrite the members through the same helper, the smallest fix is to release the previous value inside parseRangeAttribute() before assigning the new buffer. This covers both the session and subsession cases and preserves the existing partial-success (sscanfResult == 1) semantics:<br><br>    if (sscanfResult == 2) {<br>      delete[] absStartTime; absStartTime = as;<br>      delete[] absEndTime;   absEndTime = ae;<br>    } else if (sscanfResult == 1) {<br>      delete[] absStartTime; absStartTime = as;<br>      delete[] ae;<br>    } else {<br>      delete[] as; delete[] ae;<br>      return False;<br>    }<br><br>delete[] on the initial NULL members is a safe no-op, and the freshly allocated buffers are never freed after being handed to the members, so this does not introduce a double free. An alternative would be to manage ownership with a setter in each caller, but that touches more code and has to re-handle the partial-success case, so the helper-side fix above seems simpler.<br><br>Impact and limitations<br><br>  - Memory leak / potential denial-of-service (resource exhaustion) if a client parses SDP from a malicious or malfunctioning server that includes repeated "a=range: clock=" attributes, especially over long-lived or many sessions.<br>  - Reachable during SDP parsing only; no socket / SETUP required.<br>  - Not a remote code execution issue; no memory corruption was observed (leak only).<br>  - Verified with an instrumented ASan/UBSan + LeakSanitizer build; I have not re-checked runtime behavior on a plain release build, but the source is byte-identical to the 2026.06.24 release.<br><br>I'm happy to provide the full LeakSanitizer logs or a standalone non-fuzzer reproducer if that would be helpful. Thank you for maintaining LIVE555.<br><br>This finding was made as part of a university research project using resources from a collaborating laboratory.<br><br>Best regards,<br>Jason Kao (高士捷)</div>