From 54585d6caf6402a8b28993eb319d6115d6e7bea0 Mon Sep 17 00:00:00 2001 From: Davis Clark Date: Wed, 17 Jun 2026 12:05:50 -0500 Subject: [PATCH] Fix out-of-bounds crash on truncated RTMP User Control messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RTMPUserControlMessage.init reads header.payload[1] and header.payload[2..= 6 else { + event = .unknown + value = 0 + return + } + event = Event(rawValue: payload[1]) ?? .unknown + value = Int32(data: payload[2.. empty payload, position 0 == count 0, so + // makeMessage() builds the message and runs the (previously + // unguarded) decode against an empty buffer. + let header = RTMPChunkMessageHeader( + timestmap: 0, + messageLength: 0, + messageTypeId: RTMPMessageType.user.rawValue, + messageStreamId: 0 + ) + let message = header.makeMessage() as? RTMPUserControlMessage + #expect(message?.event == .unknown) + #expect(message?.value == 0) + } + + // A well-formed 6-byte user control message must still decode correctly + // (2-byte event type + 4-byte big-endian value). + @Test func wellFormedPayloadDecodes() { + let buffer = RTMPChunkBuffer() + // basic header (fmt 0, csid 2) | timestamp(3) | length(3)=6 | + // typeId(1)=4 | streamId(4) | payload: event 0x0000 (streamBegin), + // value 0x00000001. + buffer.put(Data([2, 0, 0, 0, 0, 0, 6, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])) + do { + let (chunkType, chunkStreamId) = try buffer.getBasicHeader() + #expect(chunkType == .zero) + #expect(chunkStreamId == 2) + let header = RTMPChunkMessageHeader() + try buffer.getMessageHeader(chunkType, messageHeader: header) + let message = header.makeMessage() as? RTMPUserControlMessage + #expect(message?.event == .streamBegin) + #expect(message?.value == 1) + } catch { + Issue.record("unexpected error decoding user control message: \(error)") + } + } +}