[Live-devel] Memory leak in MediaSession SDP parsing: repeated "a=range: clock=" attribute (2026.06.24)

起司 jason100076 at gmail.com
Wed Jul 22 11:32:41 PDT 2026


Hello,

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.

I found this while evaluating an AI-assisted fuzzing harness for LIVE555
SDP parsing.

Version

Verified against live.2026.06.24, which appears to be the latest available
release.

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 live555.com was returning HTTP 404 at the
time, so I obtained the same-versioned tarball via the VideoLAN mirror.

Build used for testing: clang++ 14 with
-fsanitize=address,undefined,fuzzer, LeakSanitizer enabled.

Affected functions

Both of these overwrite the member pointers fAbsStartTime / fAbsEndTime via
the shared helper parseRangeAttribute() without freeing the previous values:

  - MediaSession::parseSDPAttribute_range() (session level)
  - MediaSubsession::parseSDPAttribute_range() (media / subsession level)

Root cause

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):

    if (sscanfResult == 2) {
      absStartTime = as;   // previous value not freed
      absEndTime = ae;     // previous value not freed
    } else if (sscanfResult == 1) {
      absStartTime = as;   // previous value not freed
      delete[] ae;
    } ...

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.

Reproduction

Both cases were reproduced deterministically with an instrumented build.

(a) Session level

SDP:

    v=0
    o=- 0 0 IN IP4 127.0.0.1
    s=R
    t=0 0
    c=IN IP4 0.0.0.0
    a=range:clock=20260101T000000Z-20260101T000001Z
    a=range:clock=20260102T000000Z-20260102T000001Z
    m=video 0 RTP/AVP 96
    a=rtpmap:96 H264/90000
    a=control:track1

Result: LeakSanitizer reports 2 allocations leaked, exit code 77.

(b) Media / subsession level

SDP:

    v=0
    o=- 0 0 IN IP4 127.0.0.1
    s=R
    t=0 0
    c=IN IP4 0.0.0.0
    m=video 0 RTP/AVP 96
    a=rtpmap:96 H264/90000
    a=control:track1
    a=range:clock=20260101T000000Z-20260101T000001Z
    a=range:clock=20260102T000000Z-20260102T000001Z

Result: LeakSanitizer reports 2 allocations leaked, 194 bytes total, exit
code 77.

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.

LeakSanitizer stack traces (condensed)

Session level:

    Direct leak allocated from:
      operator new[]
      parseRangeAttribute(...)
 MediaSession.cpp:389/390
      MediaSession::parseSDPAttribute_range(...)      MediaSession.cpp:421
      MediaSession::initializeWithSDP(...)            MediaSession.cpp:122
      MediaSession::createNew(...)                    MediaSession.cpp:34

Subsession level:

    Direct leak allocated from:
      operator new[]
      parseRangeAttribute(...)
 MediaSession.cpp:389/390
      MediaSubsession::parseSDPAttribute_range(...)   MediaSession.cpp:1142
      MediaSession::initializeWithSDP(...)            MediaSession.cpp:221
      MediaSession::createNew(...)                    MediaSession.cpp:34

Suggested fix

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:

    if (sscanfResult == 2) {
      delete[] absStartTime; absStartTime = as;
      delete[] absEndTime;   absEndTime = ae;
    } else if (sscanfResult == 1) {
      delete[] absStartTime; absStartTime = as;
      delete[] ae;
    } else {
      delete[] as; delete[] ae;
      return False;
    }

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.

Impact and limitations

  - 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.
  - Reachable during SDP parsing only; no socket / SETUP required.
  - Not a remote code execution issue; no memory corruption was observed
(leak only).
  - 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.

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.

This finding was made as part of a university research project using
resources from a collaborating laboratory.

Best regards,
Jason Kao (高士捷)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.live555.com/pipermail/live-devel/attachments/20260723/4e3d2381/attachment.htm>


More information about the live-devel mailing list