Skip to content

Commit

Permalink
Add separate LC_ASSERT_VT() for asserts that only apply for valid tra…
Browse files Browse the repository at this point in the history
…ffic
  • Loading branch information
cgutman committed Oct 12, 2023
1 parent 574ad6e commit 620b4be
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 74 deletions.
10 changes: 5 additions & 5 deletions src/AudioStream.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,22 +197,22 @@ static void decodeInputData(PQUEUED_AUDIO_PACKET packet) {
(unsigned char*)(rtp + 1), dataLength,
decryptedOpusData, &dataLength)) {
Limelog("Failed to decrypt audio packet (sequence number: %u)\n", rtp->sequenceNumber);
LC_ASSERT(false);
LC_ASSERT_VT(false);
return;
}

#ifdef LC_DEBUG
if (opusHeaderByte == INVALID_OPUS_HEADER) {
opusHeaderByte = decryptedOpusData[0];
LC_ASSERT(opusHeaderByte != INVALID_OPUS_HEADER);
LC_ASSERT_VT(opusHeaderByte != INVALID_OPUS_HEADER);
}
else {
// Opus header should stay constant for the entire stream.
// If it doesn't, it may indicate that the RtpAudioQueue
// incorrectly recovered a data shard or the decryption
// of the audio packet failed. Sunshine violates this for
// surround sound in some cases, so just ignore it.
LC_ASSERT(decryptedOpusData[0] == opusHeaderByte || IS_SUNSHINE());
LC_ASSERT_VT(decryptedOpusData[0] == opusHeaderByte || IS_SUNSHINE());
}
#endif

Expand All @@ -222,13 +222,13 @@ static void decodeInputData(PQUEUED_AUDIO_PACKET packet) {
#ifdef LC_DEBUG
if (opusHeaderByte == INVALID_OPUS_HEADER) {
opusHeaderByte = ((uint8_t*)(rtp + 1))[0];
LC_ASSERT(opusHeaderByte != INVALID_OPUS_HEADER);
LC_ASSERT_VT(opusHeaderByte != INVALID_OPUS_HEADER);
}
else {
// Opus header should stay constant for the entire stream.
// If it doesn't, it may indicate that the RtpAudioQueue
// incorrectly recovered a data shard.
LC_ASSERT(((uint8_t*)(rtp + 1))[0] == opusHeaderByte);
LC_ASSERT_VT(((uint8_t*)(rtp + 1))[0] == opusHeaderByte);
}
#endif

Expand Down
4 changes: 2 additions & 2 deletions src/ControlStream.c
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ void connectionSendFrameFecStatus(PSS_FRAME_FEC_STATUS fecStatus) {
}

void connectionSawFrame(int frameIndex) {
LC_ASSERT(!isBefore16(frameIndex, lastSeenFrame));
LC_ASSERT_VT(!isBefore16(frameIndex, lastSeenFrame));

uint64_t now = PltGetMillis();

Expand Down Expand Up @@ -1123,7 +1123,7 @@ static void controlReceiveThreadFunc(void* context) {
}
else {
// What do we do here???
LC_ASSERT(false);
LC_ASSERT_VT(false);
packetLength = (int)event.packet->dataLength;
event.packet->data = NULL;
}
Expand Down
11 changes: 11 additions & 0 deletions src/Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@
#define LC_ASSERT(x) assert(x)
#endif

// If we're fuzzing, we don't want to enable asserts that can be affected by
// bad input from the remote host. LC_ASSERT_VT() is used for assertions that
// check data that comes from the host. These checks are enabled for normal
// debug builds, since they indicate an error in Moonlight or on the host.
// These are disabled when fuzzing, since the traffic is intentionally invalid.
#ifdef LC_FUZZING
#define LC_ASSERT_VT(x)
#else
#define LC_ASSERT_VT(x) LC_ASSERT(x)
#endif

#ifdef _MSC_VER
#pragma intrinsic(_byteswap_ushort)
#define BSWAP16(x) _byteswap_ushort(x)
Expand Down
42 changes: 21 additions & 21 deletions src/RtpAudioQueue.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "Limelight-internal.h"

#ifdef LC_DEBUG
#if defined(LC_DEBUG) && !defined(LC_FUZZING)
// This enables FEC validation mode with a synthetic drop
// and recovered packet checks vs the original input. It
// is on by default for debug builds.
Expand Down Expand Up @@ -86,12 +86,12 @@ static void validateFecBlockState(PRTP_AUDIO_QUEUE queue) {
while (block != NULL) {
// Ensure the list is sorted correctly
LC_ASSERT(isBefore16(lastSeqNum, block->fecHeader.baseSequenceNumber));
LC_ASSERT(isBefore32(lastTs, block->fecHeader.baseTimestamp));
LC_ASSERT_VT(isBefore32(lastTs, block->fecHeader.baseTimestamp));

// Ensure entry invariants are satisfied
LC_ASSERT(block->blockSize == lastBlock->blockSize);
LC_ASSERT(block->fecHeader.payloadType == lastBlock->fecHeader.payloadType);
LC_ASSERT(block->fecHeader.ssrc == lastBlock->fecHeader.ssrc);
LC_ASSERT_VT(block->blockSize == lastBlock->blockSize);
LC_ASSERT_VT(block->fecHeader.payloadType == lastBlock->fecHeader.payloadType);
LC_ASSERT_VT(block->fecHeader.ssrc == lastBlock->fecHeader.ssrc);

// Ensure the list itself is consistent
LC_ASSERT(block->prev == lastBlock);
Expand Down Expand Up @@ -205,7 +205,7 @@ static PRTPA_FEC_BLOCK getFecBlockForRtpPacket(PRTP_AUDIO_QUEUE queue, PRTP_PACK
if (packet->packetType == RTP_PAYLOAD_TYPE_AUDIO) {
if (length < sizeof(RTP_PACKET)) {
Limelog("RTP audio data packet too small: %u\n", length);
LC_ASSERT(false);
LC_ASSERT_VT(false);
return NULL;
}

Expand Down Expand Up @@ -239,7 +239,7 @@ static PRTPA_FEC_BLOCK getFecBlockForRtpPacket(PRTP_AUDIO_QUEUE queue, PRTP_PACK

if (length < sizeof(RTP_PACKET) + sizeof(AUDIO_FEC_HEADER)) {
Limelog("RTP audio FEC packet too small: %u\n", length);
LC_ASSERT(false);
LC_ASSERT_VT(false);
return NULL;
}

Expand All @@ -253,7 +253,7 @@ static PRTPA_FEC_BLOCK getFecBlockForRtpPacket(PRTP_AUDIO_QUEUE queue, PRTP_PACK
// later during recovery.
if (fecHeader->fecShardIndex >= RTPA_FEC_SHARDS) {
Limelog("Too many audio FEC shards: %u\n", fecHeader->fecShardIndex);
LC_ASSERT(false);
LC_ASSERT_VT(false);
return NULL;
}

Expand All @@ -264,7 +264,7 @@ static PRTPA_FEC_BLOCK getFecBlockForRtpPacket(PRTP_AUDIO_QUEUE queue, PRTP_PACK
Limelog("Invalid FEC block base sequence number (got %u, expected %u)\n",
fecBlockBaseSeqNum, (fecBlockBaseSeqNum / RTPA_DATA_SHARDS) * RTPA_DATA_SHARDS);
Limelog("Audio FEC has been disabled due to an incompatibility with your host's old software!\n");
LC_ASSERT(fecBlockBaseSeqNum % RTPA_DATA_SHARDS == 0);
LC_ASSERT_VT(fecBlockBaseSeqNum % RTPA_DATA_SHARDS == 0);
queue->incompatibleServer = true;
return NULL;
}
Expand All @@ -273,7 +273,7 @@ static PRTPA_FEC_BLOCK getFecBlockForRtpPacket(PRTP_AUDIO_QUEUE queue, PRTP_PACK
}
else {
Limelog("Invalid RTP audio payload type: %u\n", packet->packetType);
LC_ASSERT(false);
LC_ASSERT_VT(false);
return NULL;
}

Expand All @@ -296,17 +296,17 @@ static PRTPA_FEC_BLOCK getFecBlockForRtpPacket(PRTP_AUDIO_QUEUE queue, PRTP_PACK
while (existingBlock != NULL) {
if (existingBlock->fecHeader.baseSequenceNumber == fecBlockBaseSeqNum) {
// The FEC header data should match for all packets
LC_ASSERT(existingBlock->fecHeader.payloadType == fecBlockPayloadType);
LC_ASSERT(existingBlock->fecHeader.baseTimestamp == fecBlockBaseTs);
LC_ASSERT(existingBlock->fecHeader.ssrc == fecBlockSsrc);
LC_ASSERT_VT(existingBlock->fecHeader.payloadType == fecBlockPayloadType);
LC_ASSERT_VT(existingBlock->fecHeader.baseTimestamp == fecBlockBaseTs);
LC_ASSERT_VT(existingBlock->fecHeader.ssrc == fecBlockSsrc);

// The block size must match in order to safely copy shards into it
if (existingBlock->blockSize != blockSize) {
// This can happen with older versions of GeForce Experience (3.13) and Sunshine that don't use a
// constant size for audio packets.
Limelog("Audio block size mismatch (got %u, expected %u)\n", blockSize, existingBlock->blockSize);
Limelog("Audio FEC has been disabled due to an incompatibility with your host's old software!\n");
LC_ASSERT(existingBlock->blockSize == blockSize);
LC_ASSERT_VT(existingBlock->blockSize == blockSize);
queue->incompatibleServer = true;
return NULL;
}
Expand Down Expand Up @@ -464,11 +464,11 @@ static bool completeFecBlock(PRTP_AUDIO_QUEUE queue, PRTPA_FEC_BLOCK block) {

#ifdef FEC_VALIDATION_MODE
// Check the RTP header values
LC_ASSERT(block->dataPackets[dropIndex]->header == droppedRtpPacket->header);
LC_ASSERT(block->dataPackets[dropIndex]->packetType == droppedRtpPacket->packetType);
LC_ASSERT(block->dataPackets[dropIndex]->sequenceNumber == droppedRtpPacket->sequenceNumber);
LC_ASSERT(block->dataPackets[dropIndex]->timestamp == droppedRtpPacket->timestamp);
LC_ASSERT(block->dataPackets[dropIndex]->ssrc == droppedRtpPacket->ssrc);
LC_ASSERT_VT(block->dataPackets[dropIndex]->header == droppedRtpPacket->header);
LC_ASSERT_VT(block->dataPackets[dropIndex]->packetType == droppedRtpPacket->packetType);
LC_ASSERT_VT(block->dataPackets[dropIndex]->sequenceNumber == droppedRtpPacket->sequenceNumber);
LC_ASSERT_VT(block->dataPackets[dropIndex]->timestamp == droppedRtpPacket->timestamp);
LC_ASSERT_VT(block->dataPackets[dropIndex]->ssrc == droppedRtpPacket->ssrc);

// Check the data itself - use memcmp() and only loop if an error is detected
if (memcmp(block->dataPackets[dropIndex] + 1, droppedRtpPacket + 1, block->blockSize)) {
Expand All @@ -484,7 +484,7 @@ static bool completeFecBlock(PRTP_AUDIO_QUEUE queue, PRTPA_FEC_BLOCK block) {
}
}

LC_ASSERT(recoveryErrors == 0);
LC_ASSERT_VT(recoveryErrors == 0);
}

free(droppedRtpPacket);
Expand Down Expand Up @@ -523,7 +523,7 @@ static void handleMissingPackets(PRTP_AUDIO_QUEUE queue) {
// If we reach this point, we know the next packet resides in the first FEC block we're
// currently waiting on. In that case, we want to wait at least until we have a second FEC
// block to give up on the first one. If we don't have a second block now, just keep waiting.
LC_ASSERT(isBefore16(queue->nextRtpSequenceNumber, queue->blockHead->fecHeader.baseSequenceNumber + RTPA_DATA_SHARDS));
LC_ASSERT_VT(isBefore16(queue->nextRtpSequenceNumber, queue->blockHead->fecHeader.baseSequenceNumber + RTPA_DATA_SHARDS));
if (queue->blockHead == queue->blockTail) {
return;
}
Expand Down
42 changes: 22 additions & 20 deletions src/RtpVideoQueue.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "Limelight-internal.h"
#include "rs.h"

#ifdef LC_DEBUG
#if defined(LC_DEBUG) && !defined(LC_FUZZING)
// This enables FEC validation mode with a synthetic drop
// and recovered packet checks vs the original input. It
// is on by default for debug builds.
Expand Down Expand Up @@ -376,15 +376,15 @@ static int reconstructFrame(PRTP_VIDEO_QUEUE queue) {
int j;
int recoveryErrors = 0;

LC_ASSERT(droppedDataLength <= recoveredDataLength);
LC_ASSERT(droppedDataLength == recoveredDataLength || (nvPacket->flags & FLAG_EOF));
LC_ASSERT_VT(droppedDataLength <= recoveredDataLength);
LC_ASSERT_VT(droppedDataLength == recoveredDataLength || (nvPacket->flags & FLAG_EOF));

// Check all NV_VIDEO_PACKET fields except FEC stuff which differs in the recovered packet
LC_ASSERT(nvPacket->flags == droppedNvPacket->flags);
LC_ASSERT(nvPacket->frameIndex == droppedNvPacket->frameIndex);
LC_ASSERT(nvPacket->streamPacketIndex == droppedNvPacket->streamPacketIndex);
LC_ASSERT(nvPacket->reserved == droppedNvPacket->reserved);
LC_ASSERT(!queue->multiFecCapable || nvPacket->multiFecBlocks == droppedNvPacket->multiFecBlocks);
LC_ASSERT_VT(nvPacket->flags == droppedNvPacket->flags);
LC_ASSERT_VT(nvPacket->frameIndex == droppedNvPacket->frameIndex);
LC_ASSERT_VT(nvPacket->streamPacketIndex == droppedNvPacket->streamPacketIndex);
LC_ASSERT_VT(nvPacket->reserved == droppedNvPacket->reserved);
LC_ASSERT_VT(!queue->multiFecCapable || nvPacket->multiFecBlocks == droppedNvPacket->multiFecBlocks);

// Check the data itself - use memcmp() and only loop if an error is detected
if (memcmp(nvPacket + 1, droppedNvPacket + 1, droppedDataLength)) {
Expand All @@ -409,7 +409,7 @@ static int reconstructFrame(PRTP_VIDEO_QUEUE queue) {
}
}

LC_ASSERT(recoveryErrors == 0);
LC_ASSERT_VT(recoveryErrors == 0);

// This drop was fake, so we don't want to actually submit it to the depacketizer.
// It will get confused because it's already seen this packet before.
Expand Down Expand Up @@ -542,7 +542,7 @@ int RtpvAddPacket(PRTP_VIDEO_QUEUE queue, PRTP_PACKET packet, int length, PRTPV_
}

// FLAG_EXTENSION is required for all supported versions of GFE.
LC_ASSERT(packet->header & FLAG_EXTENSION);
LC_ASSERT_VT(packet->header & FLAG_EXTENSION);

int dataOffset = sizeof(*packet);
if (packet->header & FLAG_EXTENSION) {
Expand All @@ -567,11 +567,13 @@ int RtpvAddPacket(PRTP_VIDEO_QUEUE queue, PRTP_PACKET packet, int length, PRTPV_
nvPacket->multiFecFlags = 0x10;
nvPacket->multiFecBlocks = 0x00;
}


#ifndef LC_FUZZING
if (isBefore16(nvPacket->frameIndex, queue->currentFrameNumber)) {
// Reject frames behind our current frame number
return RTPF_RET_REJECTED;
}
#endif

uint32_t fecIndex = (nvPacket->fecInfo & 0x3FF000) >> 12;
uint8_t fecCurrentBlockNumber = (nvPacket->multiFecBlocks >> 4) & 0x3;
Expand Down Expand Up @@ -666,7 +668,7 @@ int RtpvAddPacket(PRTP_VIDEO_QUEUE queue, PRTP_PACKET packet, int length, PRTPV_
// The check here looks weird, but that's because we increment the frame number
// after successfully processing a frame.
if (queue->currentFrameNumber != nvPacket->frameIndex) {
LC_ASSERT(queue->currentFrameNumber < nvPacket->frameIndex);
LC_ASSERT_VT(queue->currentFrameNumber < nvPacket->frameIndex);

// If the frame immediately preceding this one was lost, we may have already
// reported it using our speculative RFI logic. Don't report it again.
Expand Down Expand Up @@ -707,19 +709,19 @@ int RtpvAddPacket(PRTP_VIDEO_QUEUE queue, PRTP_PACKET packet, int length, PRTPV_
return RTPF_RET_REJECTED;
}

LC_ASSERT(!queue->fecPercentage || U16(packet->sequenceNumber - fecIndex) == queue->bufferLowestSequenceNumber);
LC_ASSERT((nvPacket->fecInfo & 0xFF0) >> 4 == queue->fecPercentage);
LC_ASSERT((nvPacket->fecInfo & 0xFFC00000) >> 22 == queue->bufferDataPackets);
LC_ASSERT_VT(!queue->fecPercentage || U16(packet->sequenceNumber - fecIndex) == queue->bufferLowestSequenceNumber);
LC_ASSERT_VT((nvPacket->fecInfo & 0xFF0) >> 4 == queue->fecPercentage);
LC_ASSERT_VT((nvPacket->fecInfo & 0xFFC00000) >> 22 == queue->bufferDataPackets);

// Verify that the legacy non-multi-FEC compatibility code works
LC_ASSERT(queue->multiFecCapable || fecCurrentBlockNumber == 0);
LC_ASSERT(queue->multiFecCapable || queue->multiFecLastBlockNumber == 0);
LC_ASSERT_VT(queue->multiFecCapable || fecCurrentBlockNumber == 0);
LC_ASSERT_VT(queue->multiFecCapable || queue->multiFecLastBlockNumber == 0);

// Multi-block FEC details must remain the same within a single frame
LC_ASSERT(fecCurrentBlockNumber == queue->multiFecCurrentBlockNumber);
LC_ASSERT(((nvPacket->multiFecBlocks >> 6) & 0x3) == queue->multiFecLastBlockNumber);
LC_ASSERT_VT(fecCurrentBlockNumber == queue->multiFecCurrentBlockNumber);
LC_ASSERT_VT(((nvPacket->multiFecBlocks >> 6) & 0x3) == queue->multiFecLastBlockNumber);

LC_ASSERT((nvPacket->flags & FLAG_EOF) || length - dataOffset == StreamConfig.packetSize);
LC_ASSERT_VT((nvPacket->flags & FLAG_EOF) || length - dataOffset == StreamConfig.packetSize);
if (!queuePacket(queue, packetEntry, packet, length, !isBefore16(packet->sequenceNumber, queue->bufferFirstParitySequenceNumber), false)) {
return RTPF_RET_REJECTED;
}
Expand Down
Loading

0 comments on commit 620b4be

Please sign in to comment.