Skip to content

Fix out-of-bounds crash on truncated RTMP User Control messages#1922

Open
davissclark wants to merge 1 commit into
HaishinKit:mainfrom
wecadre:cadre/rtmp-usercontrol-bounds-upstream
Open

Fix out-of-bounds crash on truncated RTMP User Control messages#1922
davissclark wants to merge 1 commit into
HaishinKit:mainfrom
wecadre:cadre/rtmp-usercontrol-bounds-upstream

Conversation

@davissclark

Copy link
Copy Markdown

Problem

RTMPUserControlMessage.init(_:) decodes a User Control message without validating the payload length:

event = Event(rawValue: header.payload[1]) ?? .unknown
value = Int32(data: header.payload[2..<header.payload.count]).bigEndian

A well-formed User Control message is at least 6 bytes (2-byte event type + 4-byte value). When the peer sends a shorter/truncated message, payload[1] / the 2..<count slice / the 4-byte Int32 read index past the end of the Data, and Swift's bounds check traps:

Exception Type:  EXC_BREAKPOINT (SIGTRAP)
Thread N Crashed:
0  Foundation   Data._Representation.subscript.getter
1  …            RTMPUserControlMessage.init(_:) (RTMPMessage.swift)
2  …            RTMPChunkMessageHeader.makeMessage() (RTMPChunk.swift)
3  …            RTMPConnection.listen(_:) (RTMPConnection.swift)

Because this is on the RTMPConnection receive loop, a single malformed inbound message crashes the entire host process. We hit this in production on a publishing client.

Fix

Re-base the payload to a 0-indexed copy (a Data slice keeps its parent's indices, so even payload[1] is unsafe on a non-zero-based slice) and fall back to a safe default (.unknown / 0) when the payload is shorter than the 6-byte layout. Behavior for valid messages is unchanged.

Tests

Adds RTMPUserControlMessageTests:

  • truncatedPayloadDoesNotCrash — a 0-length user-control payload. Before the fix this test process exits with signal 5 (SIGTRAP); after, it returns .unknown / 0.
  • wellFormedPayloadDecodes — a valid 6-byte streamBegin message still decodes to event == .streamBegin, value == 1.

🤖 Generated with Claude Code

RTMPUserControlMessage.init reads header.payload[1] and
header.payload[2..<header.payload.count] without validating the payload
length. A User Control message shorter than its 6-byte layout (2-byte event
type + 4-byte value) — e.g. a truncated or malformed message from the peer —
indexes past the end of the Data and traps (EXC_BREAKPOINT / fatal
out-of-bounds) on the RTMPConnection receive loop, crashing the host process.

Re-base the payload to a 0-indexed copy (a Data slice keeps its parent's
indices) and fall back to a safe default (.unknown / 0) when the payload is
too short. Adds regression tests for the truncated and well-formed cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant