From 601beef6f5b5d1d87559149df4ab44bdcc65ca79 Mon Sep 17 00:00:00 2001 From: Mounika HJ Date: Thu, 6 Aug 2026 17:26:10 +0530 Subject: [PATCH 1/2] fix: bound RTP header offsets in voice_courier_loop to packet size --- src/dpp/voice/enabled/courier_loop.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/dpp/voice/enabled/courier_loop.cpp b/src/dpp/voice/enabled/courier_loop.cpp index 055a37d6f7..2e2056232b 100644 --- a/src/dpp/voice/enabled/courier_loop.cpp +++ b/src/dpp/voice/enabled/courier_loop.cpp @@ -179,6 +179,17 @@ void discord_voice_client::voice_courier_loop(discord_voice_client& client, cour const size_t csrc_count = buffer[0] & 0b0000'1111; /* Skip to the encrypted voice data */ const ptrdiff_t offset_to_data = header_size + sizeof(uint32_t) * csrc_count; + + /* + * csrc_count comes from the first byte of the datagram, so the packet + * has to actually be long enough to hold the header it describes plus the + * trailing nonce, otherwise ciphertext_len below underflows. + */ + if (packet_size < static_cast(offset_to_data) + nonce_size) { + /* Invalid Discord RTP payload. */ + return; + } + size_t total_header_len = offset_to_data; uint8_t *ciphertext = buffer + offset_to_data; @@ -186,6 +197,14 @@ void discord_voice_client::voice_courier_loop(discord_voice_client& client, cour size_t ext_len = 0; if ([[maybe_unused]] const bool uses_extension = (buffer[0] >> 4) & 0b0001) { + /* + * The 4 byte extension header sits past offset_to_data, so make sure + * it is present before reading it and subtracting it from ciphertext_len. + */ + if (ciphertext_len < sizeof(uint16_t) * 2) { + /* Invalid Discord RTP payload. */ + return; + } /** * Get the RTP Extensions size, we only get the size here because * the extension itself is encrypted along with the opus packet From b3f7462b866c9979257e7de1f4df34932e94f373 Mon Sep 17 00:00:00 2001 From: Mounika HJ Date: Thu, 27 Aug 2026 15:20:04 +0530 Subject: [PATCH 2/2] fix: drop malformed RTP packet instead of ending courier thread Signed-off-by: Mounika HJ --- src/dpp/voice/enabled/courier_loop.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/dpp/voice/enabled/courier_loop.cpp b/src/dpp/voice/enabled/courier_loop.cpp index 2e2056232b..3ac3274add 100644 --- a/src/dpp/voice/enabled/courier_loop.cpp +++ b/src/dpp/voice/enabled/courier_loop.cpp @@ -186,8 +186,12 @@ void discord_voice_client::voice_courier_loop(discord_voice_client& client, cour * trailing nonce, otherwise ciphertext_len below underflows. */ if (packet_size < static_cast(offset_to_data) + nonce_size) { - /* Invalid Discord RTP payload. */ - return; + /* + * Invalid Discord RTP payload. Consume it and move on to the + * next parked payload, like the pop at the end of this iteration. + */ + d.parked_payloads.pop(); + continue; } size_t total_header_len = offset_to_data; @@ -202,8 +206,12 @@ void discord_voice_client::voice_courier_loop(discord_voice_client& client, cour * it is present before reading it and subtracting it from ciphertext_len. */ if (ciphertext_len < sizeof(uint16_t) * 2) { - /* Invalid Discord RTP payload. */ - return; + /* + * Invalid Discord RTP payload. Consume it and move on to the + * next parked payload, like the pop at the end of this iteration. + */ + d.parked_payloads.pop(); + continue; } /** * Get the RTP Extensions size, we only get the size here because