From 8cb7e344769cd3949b138a456659251991ee7915 Mon Sep 17 00:00:00 2001 From: Anthony Rossi <41394064+anrossi@users.noreply.github.com> Date: Wed, 15 Apr 2020 16:05:51 -0700 Subject: [PATCH] Implement Frame decode failure tests (#315) --- src/core/frame.c | 3 +- src/core/unittest/FrameTest.cpp | 841 +++++++++++++++++++++++++++++++- 2 files changed, 839 insertions(+), 5 deletions(-) diff --git a/src/core/frame.c b/src/core/frame.c index 465ec6e612..12d57eaf64 100644 --- a/src/core/frame.c +++ b/src/core/frame.c @@ -104,7 +104,8 @@ QuicAckHeaderDecode( if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->LargestAcknowledged) || !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->AckDelay) || !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->AdditionalAckBlockCount) || - !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->FirstAckBlock)) { + !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->FirstAckBlock) || + Frame->FirstAckBlock > Frame->LargestAcknowledged) { return FALSE; } return TRUE; diff --git a/src/core/unittest/FrameTest.cpp b/src/core/unittest/FrameTest.cpp index 775aa0b502..ab32b968fc 100644 --- a/src/core/unittest/FrameTest.cpp +++ b/src/core/unittest/FrameTest.cpp @@ -15,16 +15,198 @@ #include "frametest.tmh" #endif -TEST(FrameTest, AckFrameEncodeDecode) +struct AckFrameTest : ::testing::TestWithParam { +}; + +TEST_P(AckFrameTest, AckFrameEncodeDecode) { + const uint64_t MaxPktNum = 10; + const uint64_t ContigPktCount = 4; + const uint64_t MinPktNum = 5; + const uint64_t AckDelay = 0; + QUIC_ACK_ECN_EX Ecn = {4, 4, 4}; + QUIC_ACK_ECN_EX DecodedEcn = {0, 0, 0}; QUIC_RANGE AckRange; + QUIC_RANGE DecodedAckRange; + uint8_t Buffer[10]; + uint16_t BufferLength = (uint16_t) sizeof(Buffer); + uint16_t Offset = 0; + uint64_t DecodedAckDelay = 0; + uint64_t RangeLength = 0; + BOOLEAN InvalidFrame = FALSE; + BOOLEAN IsLastRange = FALSE; + BOOLEAN Unused; + + QuicZeroMemory(Buffer, sizeof(Buffer)); + + TEST_QUIC_SUCCEEDED(QuicRangeInitialize(QUIC_MAX_RANGE_DECODE_ACKS, &AckRange)); + TEST_QUIC_SUCCEEDED(QuicRangeInitialize(QUIC_MAX_RANGE_DECODE_ACKS, &DecodedAckRange)); + + ASSERT_TRUE(QuicRangeAddRange(&AckRange, MinPktNum, ContigPktCount, &Unused) != nullptr); + ASSERT_TRUE(QuicRangeAddValue(&AckRange, MaxPktNum)); + + ASSERT_TRUE(QuicAckFrameEncode(&AckRange, AckDelay, (GetParam() == QUIC_FRAME_ACK ? nullptr : &Ecn), &Offset, BufferLength, Buffer)); + Offset = 1; + ASSERT_EQ(Buffer[0], GetParam()); + ASSERT_TRUE(QuicAckFrameDecode(GetParam(), BufferLength, Buffer, &Offset, &InvalidFrame, &DecodedAckRange, &DecodedEcn, &DecodedAckDelay)); + + ASSERT_FALSE(InvalidFrame); + ASSERT_EQ(AckDelay, DecodedAckDelay); + ASSERT_EQ(QuicRangeSize(&DecodedAckRange), QuicRangeSize(&AckRange)); + ASSERT_EQ(QuicRangeGetMin(&DecodedAckRange), MinPktNum); + ASSERT_EQ(QuicRangeGetMax(&DecodedAckRange), MaxPktNum); + ASSERT_TRUE(QuicRangeGetRange(&DecodedAckRange, MinPktNum, &RangeLength, &IsLastRange)); + ASSERT_EQ(RangeLength, ContigPktCount); + ASSERT_FALSE(IsLastRange); + ASSERT_TRUE(QuicRangeGetRange(&DecodedAckRange, MaxPktNum, &RangeLength, &IsLastRange)); + ASSERT_EQ(RangeLength, 1); + ASSERT_TRUE(IsLastRange); + + if (GetParam() == QUIC_FRAME_ACK_1) { + ASSERT_EQ(Ecn.CE_Count, DecodedEcn.CE_Count); + ASSERT_EQ(Ecn.ECT_0_Count, DecodedEcn.ECT_0_Count); + ASSERT_EQ(Ecn.ECT_1_Count, DecodedEcn.ECT_1_Count); + } + + QuicRangeUninitialize(&AckRange); + QuicRangeUninitialize(&DecodedAckRange); +} + +TEST_P(AckFrameTest, DecodeAckFrameFail) { + QUIC_ACK_ECN_EX DecodedEcn; + uint8_t Buffer[18]; + uint16_t BufferLength; + uint16_t Offset = 1; + BOOLEAN InvalidFrame = FALSE; + QUIC_RANGE DecodedAckBlocks; + QUIC_VAR_INT AckDelay = 0; + TEST_QUIC_SUCCEEDED(QuicRangeInitialize(QUIC_MAX_RANGE_DECODE_ACKS, &DecodedAckBlocks)); + Buffer[0] = (uint8_t)GetParam(); - TEST_QUIC_SUCCEEDED(QuicRangeInitialize((uint32_t)~0, &AckRange)); // - // TODO: Write test case + // Test Case: ACK Range count > number of ACK ranges + // + BufferLength = 7; + Buffer[1] = 10; // Highest ACKed PN + Buffer[2] = 1; // ACK Delay + Buffer[3] = 3; // ACK range count + Buffer[4] = 4; // First ACK range + Buffer[5] = 4; // First ACK gap + Buffer[6] = 0; // Second ACK range + + if (GetParam() == QUIC_FRAME_ACK_1) { + BufferLength += 3; + Buffer[7] = 1; + Buffer[8] = 2; + Buffer[9] = 3; + } + + BOOLEAN Result = QuicAckFrameDecode(GetParam(), BufferLength, Buffer, &Offset, &InvalidFrame, &DecodedAckBlocks, &DecodedEcn, &AckDelay); + + ASSERT_TRUE(InvalidFrame); + ASSERT_FALSE(Result); + QuicRangeReset(&DecodedAckBlocks); + // + // Test Case: ACK Range count > Highest ACKed PN + // + Offset = 1; + InvalidFrame = FALSE; + BufferLength = 15; + Buffer[1] = 4; // Highest ACKed PN + Buffer[2] = 1; // ACK Delay + Buffer[3] = 5; // ACK range count + Buffer[4] = 0; // First ACK range + Buffer[5] = 0; // First ACK gap + Buffer[6] = 0; // Second ACK Range + Buffer[7] = 0; // Second ACK Gap + Buffer[8] = 0; // Third ACK Range + Buffer[9] = 0; // Third ACK Gap + Buffer[10] = 0; // Fourth ACK Range + Buffer[11] = 0; // Fourth ACK Gap + Buffer[12] = 0; // Fifth ACK Range + Buffer[13] = 0; // Fifth ACK Gap + Buffer[14] = 0; // Sixth ACK Range + + if (GetParam() == QUIC_FRAME_ACK_1) { + BufferLength += 3; + Buffer[15] = 4; + Buffer[16] = 5; + Buffer[17] = 6; + } + + Result = QuicAckFrameDecode(GetParam(), BufferLength, Buffer, &Offset, &InvalidFrame, &DecodedAckBlocks, &DecodedEcn, &AckDelay); + + ASSERT_TRUE(InvalidFrame); + ASSERT_FALSE(Result); + QuicRangeReset(&DecodedAckBlocks); + + // + // Test Case: First ACK range > Highest ACKed PN + // + Offset = 1; + InvalidFrame = FALSE; + BufferLength = 5; + Buffer[1] = 5; // Highest ACKed PN + Buffer[2] = 1; // ACK Delay + Buffer[3] = 0; // ACK range count + Buffer[4] = 6; // First ACK range + + if (GetParam() == QUIC_FRAME_ACK_1) { + BufferLength += 3; + Buffer[5] = 7; + Buffer[6] = 8; + Buffer[7] = 9; + } + + Result = QuicAckFrameDecode(GetParam(), BufferLength, Buffer, &Offset, &InvalidFrame, &DecodedAckBlocks, &DecodedEcn, &AckDelay); + + ASSERT_TRUE(InvalidFrame); + ASSERT_FALSE(Result); + QuicRangeReset(&DecodedAckBlocks); + + // + // Test Case: ECN fields contain improperly-formatted QUIC VAR INTs. + // + if (GetParam() == QUIC_FRAME_ACK_1) { + BufferLength = 8; + Buffer[1] = 5; // Highest ACKed PN + Buffer[2] = 1; // ACK Delay + Buffer[3] = 0; // ACK range count + Buffer[4] = 5; // First ACK range + for (auto TestValue : {64, 255}) { + for (uint8_t i = 1; i < 8; ++i) { + Offset = 1; + InvalidFrame = FALSE; + // + // ECT(0) COunt + // + Buffer[5] = (i & 1) ? TestValue : 0; + // + // ECT(1) Count + // + Buffer[6] = (i & 2) ? TestValue : 0; + // + // ECN-CE Count + // + Buffer[7] = (i & 4) ? TestValue : 0; + + ASSERT_FALSE(QuicAckFrameDecode(GetParam(), BufferLength, Buffer, &Offset, &InvalidFrame, &DecodedAckBlocks, &DecodedEcn, &AckDelay)); + + QuicRangeReset(&DecodedAckBlocks); + } + } + } + + QuicRangeUninitialize(&DecodedAckBlocks); } +INSTANTIATE_TEST_SUITE_P( + FrameTest, + AckFrameTest, + ::testing::Values(QUIC_FRAME_ACK, QUIC_FRAME_ACK_1), + ::testing::PrintToStringParamName()); + TEST(FrameTest, ResetStreamFrameEncodeDecode) { QUIC_RESET_STREAM_EX Frame = {127, 4294967297, 65536}; @@ -43,6 +225,58 @@ TEST(FrameTest, ResetStreamFrameEncodeDecode) ASSERT_EQ(Frame.FinalSize, DecodedFrame.FinalSize); } +struct ResetStreamFrameParams { + uint8_t Buffer[4]; + uint16_t BufferLength = 4; + + static auto GenerateDecodeFailParams() { + std::vector Params; + const uint16_t BufferLength = 4; + for (uint32_t i = 1; i < 8; ++i) { + ResetStreamFrameParams Temp; + Temp.Buffer[0] = QUIC_FRAME_RESET_STREAM; + + for (uint32_t j = 0; j < 2; ++j) { + uint8_t TestValue = (j & 1) ? 64 : 255; + + if (i & 1) { + Temp.Buffer[1] = TestValue; + } else { + Temp.Buffer[1] = 0; + } + + if (i & 2) { + Temp.Buffer[2] = TestValue; + } else { + Temp.Buffer[2] = 0; + } + + if (i & 4) { + Temp.Buffer[3] = TestValue; + } else { + Temp.Buffer[3] = 0; + } + + Params.push_back(Temp); + } + } + return Params; + } +}; + +struct ResetStreamFrameTest : ::testing::TestWithParam {}; + +TEST_P(ResetStreamFrameTest, DecodeResetStreamFrameFail) { + uint16_t Offset = 1; + QUIC_RESET_STREAM_EX DecodedFrame; + ASSERT_FALSE(QuicResetStreamFrameDecode(GetParam().BufferLength, GetParam().Buffer, &Offset, &DecodedFrame)); +} + +INSTANTIATE_TEST_SUITE_P( + FrameTest, + ResetStreamFrameTest, + ::testing::ValuesIn(ResetStreamFrameParams::GenerateDecodeFailParams())); + TEST(FrameTest, StopSendingFrameEncodeDecode) { QUIC_STOP_SENDING_EX Frame = {42, 64}; @@ -60,6 +294,48 @@ TEST(FrameTest, StopSendingFrameEncodeDecode) ASSERT_EQ(Frame.ErrorCode, DecodedFrame.ErrorCode); } +struct StopSendingFrameParams { + uint8_t Buffer[3]; + uint16_t BufferLength = 3; + + static auto GenerateDecodeFailParams() { + std::vector Params; + for (uint32_t i = 1; i < 4; ++i) { + StopSendingFrameParams Temp; + Temp.Buffer[0] = QUIC_FRAME_STOP_SENDING; + + for (uint32_t j = 0; j < 2; ++j) { + uint8_t TestValue = (j & 1) ? 64 : 255; + + if (i & 1) { + Temp.Buffer[1] = TestValue; + } else { + Temp.Buffer[1] = 0; + } + + if (i & 2) { + Temp.Buffer[2] = TestValue; + } else { + Temp.Buffer[2] = 0; + } + + Params.push_back(Temp); + } + } + return Params; + } +}; + +struct StopSendingFrameTest : ::testing::TestWithParam {}; + +TEST_P(StopSendingFrameTest, DecodeStopSendingFrameFail) { + QUIC_STOP_SENDING_EX DecodedFrame; + uint16_t Offset = 1; + ASSERT_FALSE(QuicStopSendingFrameDecode(GetParam().BufferLength, GetParam().Buffer, &Offset, &DecodedFrame)); +} + +INSTANTIATE_TEST_SUITE_P(FrameTest, StopSendingFrameTest, ::testing::ValuesIn(StopSendingFrameParams::GenerateDecodeFailParams())); + TEST(FrameTest, CryptoFrameEncodeDecode) { uint8_t FrameBuf[sizeof(QUIC_CRYPTO_EX) + 3]; @@ -84,6 +360,48 @@ TEST(FrameTest, CryptoFrameEncodeDecode) ASSERT_EQ(memcmp(Frame->Data, DecodedFrame.Data, DecodedFrame.Length), 0); } +struct CryptoFrameParams { + uint8_t Buffer[7]; + uint16_t BufferLength = 6; + + static auto GenerateDecodeFailParams() { + std::vector Params; + for (uint32_t i = 1; i < 4; ++i) { + CryptoFrameParams Temp; + Temp.Buffer[0] = QUIC_FRAME_CRYPTO; + Temp.Buffer[3] = Temp.Buffer[4] = Temp.Buffer[5] = Temp.Buffer[6] = 127; + + for (uint32_t j = 0; j < 2; ++j) { + uint8_t TestValue = (j & 1) ? 64 : 255; + + if (i & 1) { + Temp.Buffer[1] = TestValue; + } else { + Temp.Buffer[1] = 0; + } + + if (i & 2) { + Temp.Buffer[2] = TestValue; + } else { + Temp.Buffer[2] = 0; + } + Params.push_back(Temp); + } + } + return Params; + } +}; + +struct CryptoFrameTest : ::testing::TestWithParam {}; + +TEST_P(CryptoFrameTest, DecodeCryptoFrameFail) { + QUIC_CRYPTO_EX DecodedFrame; + uint16_t Offset = 1; + ASSERT_FALSE(QuicCryptoFrameDecode(GetParam().BufferLength, GetParam().Buffer, &Offset, &DecodedFrame)); +} + +INSTANTIATE_TEST_SUITE_P(FrameTest, CryptoFrameTest, ::testing::ValuesIn(CryptoFrameParams::GenerateDecodeFailParams())); + TEST(FrameTest, NewTokenFrameEncodeDecode) { uint8_t FrameBuf[sizeof(QUIC_NEW_TOKEN_EX) + 3]; @@ -107,6 +425,33 @@ TEST(FrameTest, NewTokenFrameEncodeDecode) ASSERT_EQ(memcmp(Frame->Token, DecodedFrame.Token, DecodedFrame.TokenLength), 0); } +struct NewTokenFrameParams { + uint8_t Buffer[3]; + uint16_t BufferLength = 3; + + static auto GenerateDecodeFailParams() { + std::vector Params; + for (uint32_t i = 0; i < 2; ++i) { + NewTokenFrameParams Temp; + Temp.Buffer[0] = QUIC_FRAME_NEW_TOKEN; + Temp.Buffer[1] = (i & 1) ? 65 : 255; + Temp.Buffer[2] = 0; + Params.push_back(Temp); + } + return Params; + } +}; + +struct NewTokenFrameTest : ::testing::TestWithParam {}; + +TEST_P(NewTokenFrameTest, DecodeNewTokenFrameFail) { + QUIC_NEW_TOKEN_EX DecodedFrame; + uint16_t Offset = 1; + ASSERT_FALSE(QuicNewTokenFrameDecode(GetParam().BufferLength, GetParam().Buffer, &Offset, &DecodedFrame)); +} + +INSTANTIATE_TEST_SUITE_P(FrameTest, NewTokenFrameTest, ::testing::ValuesIn(NewTokenFrameParams::GenerateDecodeFailParams())); + struct StreamFrameTest : ::testing::TestWithParam { }; @@ -162,7 +507,115 @@ TEST_P(StreamFrameTest, StreamFrameEncodeDecode) ASSERT_EQ(DecodedFrame.Data, &Buffer[QuicStreamFrameHeaderSize(&DecodedFrame)]); } -INSTANTIATE_TEST_SUITE_P(FrameTest, StreamFrameTest, ::testing::Values(QUIC_FRAME_STREAM, QUIC_FRAME_STREAM_1, QUIC_FRAME_STREAM_2, QUIC_FRAME_STREAM_3, QUIC_FRAME_STREAM_4, QUIC_FRAME_STREAM_5, QUIC_FRAME_STREAM_6, QUIC_FRAME_STREAM_7), ::testing::PrintToStringParamName()); +TEST_P(StreamFrameTest, DecodeStreamFrameFail) { + const uint8_t DataLen = 1; + QUIC_STREAM_FRAME_TYPE Type; + Type.Type = (uint8_t)GetParam(); + QUIC_STREAM_EX DecodedFrame; + uint8_t Buffer[1 + 1 + 1 + 1 + DataLen]; + uint16_t BufferLength; + uint16_t Offset = 1; + + Buffer[0] = Type.Type; + + switch(GetParam()) { + case QUIC_FRAME_STREAM: + case QUIC_FRAME_STREAM_1: + QUIC_FRE_ASSERT(!Type.LEN && !Type.OFF); + for(uint8_t i = 0; i < 2; ++i) { + Offset = 1; + // + // Stream ID + // + if (i & 1) { + Buffer[1] = 255; + Buffer[2] = 0; + BufferLength = 3; + } else { + Buffer[1] = 64; + BufferLength = 2; + } + ASSERT_FALSE(QuicStreamFrameDecode(GetParam(), BufferLength, Buffer, &Offset, &DecodedFrame)) << "Test case: " << i; + } + break; + + case QUIC_FRAME_STREAM_2: // LEN + case QUIC_FRAME_STREAM_3: // LEN + FIN + case QUIC_FRAME_STREAM_4: // OFF + case QUIC_FRAME_STREAM_5: // OFF + FIN + QUIC_FRE_ASSERT(Type.LEN || Type.OFF); + for (uint8_t i = 1; i < 4; ++i) { + Offset = 1; + // + // Stream ID + // + if (i & 1) { + Buffer[1] = 255; + } else { + Buffer[1] = 0; + } + + // + // Length or Offset + // + if (i & 2) { + Buffer[2] = 255; + } else { + Buffer[2] = 0; + } + Buffer[3] = 1; + BufferLength = 4; + ASSERT_FALSE(QuicStreamFrameDecode(GetParam(), BufferLength, Buffer, &Offset, &DecodedFrame)) << "Test case: " << i; + } + break; + + case QUIC_FRAME_STREAM_6: // OFF + LEN + case QUIC_FRAME_STREAM_7: // OFF + LEN + FIN + QUIC_FRE_ASSERT(Type.LEN && Type.OFF); + for (uint8_t i = 1; i < 8; ++i) { + Offset = 1; + // + // Stream ID + // + if (i & 1) { + Buffer[1] = 255; + } else { + Buffer[1] = 0; + } + + // + // Offset + // + if (i & 2) { + Buffer[2] = 255; + } else { + Buffer[2] = 0; + } + + // + // Length + // + if (i & 4) { + Buffer[3] = 255; + } else { + Buffer[3] = 0; + } + Buffer[4] = 0; + BufferLength = 4; + ASSERT_FALSE(QuicStreamFrameDecode(GetParam(), BufferLength, Buffer, &Offset, &DecodedFrame)) << "Test case: " << i; + } + break; + default: + FAIL() << "Missing test case for " << GetParam(); + break; + } +} + +INSTANTIATE_TEST_SUITE_P( + FrameTest, + StreamFrameTest, + ::testing::Values(QUIC_FRAME_STREAM, QUIC_FRAME_STREAM_1, QUIC_FRAME_STREAM_2, QUIC_FRAME_STREAM_3, QUIC_FRAME_STREAM_4, QUIC_FRAME_STREAM_5, QUIC_FRAME_STREAM_6, QUIC_FRAME_STREAM_7), + ::testing::PrintToStringParamName()); TEST(FrameTest, MaxDataFrameEncodeDecode) { @@ -180,6 +633,32 @@ TEST(FrameTest, MaxDataFrameEncodeDecode) ASSERT_EQ(Frame.MaximumData, DecodedFrame.MaximumData); } +struct MaxDataFrameParams { + uint8_t Buffer[2]; + uint16_t BufferLength = 2; + + static auto GenerateDecodeFailParams() { + std::vector Params; + for (uint32_t i = 0; i < 2; ++i) { + MaxDataFrameParams Temp; + Temp.Buffer[0] = QUIC_FRAME_MAX_DATA; + Temp.Buffer[1] = (i & 1) ? 64 : 255; + Params.push_back(Temp); + } + return Params; + } +}; + +struct MaxDataFrameTest : ::testing::TestWithParam {}; + +TEST_P(MaxDataFrameTest, DecodeMaxDataFrameFail) { + QUIC_MAX_DATA_EX DecodedFrame; + uint16_t Offset = 1; + ASSERT_FALSE(QuicMaxDataFrameDecode(GetParam().BufferLength, GetParam().Buffer, &Offset, &DecodedFrame)); +} + +INSTANTIATE_TEST_SUITE_P(FrameTest, MaxDataFrameTest, ::testing::ValuesIn(MaxDataFrameParams::GenerateDecodeFailParams())); + TEST(FrameTest, MaxStreamDataFrameEncodeDecode) { QUIC_MAX_STREAM_DATA_EX Frame = {65, 65537}; @@ -197,6 +676,45 @@ TEST(FrameTest, MaxStreamDataFrameEncodeDecode) ASSERT_EQ(Frame.MaximumData, DecodedFrame.MaximumData); } +struct MaxStreamDataFrameParams { + uint8_t Buffer[3]; + uint16_t BufferLength = 3; + + static auto GenerateDecodeFailParams() { + std::vector Params; + for (uint32_t i = 1; i < 4; ++i) { + MaxStreamDataFrameParams Temp; + Temp.Buffer[0] = QUIC_FRAME_MAX_STREAM_DATA; + for (uint32_t j = 0; j < 2; ++j) { + uint8_t TestValue = (j & 1) ? 64 : 255; + if (i & 1) { + Temp.Buffer[1] = TestValue; + } else { + Temp.Buffer[1] = 0; + } + + if (i & 2) { + Temp.Buffer[2] = TestValue; + } else { + Temp.Buffer[2] = 0; + } + Params.push_back(Temp); + } + } + return Params; + } +}; + +struct MaxStreamDataFrameTest : ::testing::TestWithParam {}; + +TEST_P(MaxStreamDataFrameTest, DecodeMaxStreamDataFrameFail) { + QUIC_MAX_STREAM_DATA_EX DecodedFrame; + uint16_t Offset = 1; + ASSERT_FALSE(QuicMaxStreamDataFrameDecode(GetParam().BufferLength, GetParam().Buffer, &Offset, &DecodedFrame)); +} + +INSTANTIATE_TEST_SUITE_P(FrameTest, MaxStreamDataFrameTest, ::testing::ValuesIn(MaxStreamDataFrameParams::GenerateDecodeFailParams())); + struct MaxStreamsFrameTest : ::testing::TestWithParam { }; @@ -218,6 +736,19 @@ TEST_P(MaxStreamsFrameTest, MaxStreamsFrameEncodeDecode) ASSERT_EQ(Frame.MaximumStreams, DecodedFrame.MaximumStreams); } +TEST_P(MaxStreamsFrameTest, DecodeMaxStreamsFrameFail) { + const uint16_t BufferLength = 2; + QUIC_MAX_STREAMS_EX DecodedFrame; + uint8_t Buffer[BufferLength]; + uint16_t Offset; + for (uint32_t i = 0; i < 2; ++i) { + Buffer[0] = GetParam() ? QUIC_FRAME_MAX_STREAMS : QUIC_FRAME_MAX_STREAMS_1; + Buffer[1] = (i & 1) ? 64 : 255; + Offset = 1; + ASSERT_FALSE(QuicMaxStreamsFrameDecode((QUIC_FRAME_TYPE) Buffer[0], BufferLength, Buffer, &Offset, &DecodedFrame)); + } +} + INSTANTIATE_TEST_SUITE_P( FrameTest, MaxStreamsFrameTest, @@ -246,6 +777,32 @@ TEST(FrameTest, DataBlockedFrameEncodeDecode) ASSERT_EQ(Frame.DataLimit, DecodedFrame.DataLimit); } +struct DataBlockedFrameParams { + uint8_t Buffer[2]; + uint16_t BufferLength = 2; + + static auto GenerateDecodeFailParams() { + std::vector Params; + for (uint32_t i = 0; i < 2; ++i) { + DataBlockedFrameParams Temp; + Temp.Buffer[0] = QUIC_FRAME_DATA_BLOCKED; + Temp.Buffer[1] = (i & 1) ? 64 : 255; + Params.push_back(Temp); + } + return Params; + } +}; + +struct DataBlockedFrameTest : ::testing::TestWithParam {}; + +TEST_P(DataBlockedFrameTest, DecodeDataBlockedFrameFail) { + QUIC_DATA_BLOCKED_EX DecodedFrame; + uint16_t Offset = 1; + ASSERT_FALSE(QuicDataBlockedFrameDecode(GetParam().BufferLength, GetParam().Buffer, &Offset, &DecodedFrame)); +} + +INSTANTIATE_TEST_SUITE_P(FrameTest, DataBlockedFrameTest, ::testing::ValuesIn(DataBlockedFrameParams::GenerateDecodeFailParams())); + TEST(FrameTest, StreamDataBlockedFrameEncodeDecode) { QUIC_STREAM_DATA_BLOCKED_EX Frame = {127, 255}; @@ -263,6 +820,45 @@ TEST(FrameTest, StreamDataBlockedFrameEncodeDecode) ASSERT_EQ(Frame.StreamDataLimit, DecodedFrame.StreamDataLimit); } +struct StreamDataBlockedFrameParams { + uint8_t Buffer[3]; + uint16_t BufferLength = 3; + + static auto GenerateDecodeFailParams() { + std::vector Params; + for (uint32_t i = 1; i < 4; ++i) { + StreamDataBlockedFrameParams Temp; + Temp.Buffer[0] = QUIC_FRAME_STREAM_DATA_BLOCKED; + for (uint32_t j = 0; j < 2; ++j) { + uint8_t TestValue = (j & 1) ? 64 : 255; + if (i & 1) { + Temp.Buffer[1] = TestValue; + } else { + Temp.Buffer[1] = 0; + } + + if (i & 2) { + Temp.Buffer[2] = TestValue; + } else { + Temp.Buffer[2] = 0; + } + Params.push_back(Temp); + } + } + return Params; + } +}; + +struct StreamDataBlockedFrameTest : ::testing::TestWithParam {}; + +TEST_P(StreamDataBlockedFrameTest, DecodeStreamDataBlockedFrameFail) { + QUIC_STREAM_DATA_BLOCKED_EX DecodedFrame; + uint16_t Offset = 1; + ASSERT_FALSE(QuicStreamDataBlockedFrameDecode(GetParam().BufferLength, GetParam().Buffer, &Offset, &DecodedFrame)); +} + +INSTANTIATE_TEST_SUITE_P(FrameTest, StreamDataBlockedFrameTest, ::testing::ValuesIn(StreamDataBlockedFrameParams::GenerateDecodeFailParams())); + struct StreamsBlockedFrameTest : ::testing::TestWithParam { }; @@ -284,6 +880,19 @@ TEST_P(StreamsBlockedFrameTest, StreamsBlockedFrameEncodeDecode) ASSERT_EQ(Frame.StreamLimit, DecodedFrame.StreamLimit); } +TEST_P(StreamsBlockedFrameTest, DecodeStreamsBlockedFrameFail) { + const uint16_t BufferLength = 2; + QUIC_STREAMS_BLOCKED_EX DecodedFrame; + uint8_t Buffer[BufferLength]; + uint16_t Offset; + for (uint32_t i = 0; i < 2; ++i) { + Buffer[0] = GetParam() ? QUIC_FRAME_STREAMS_BLOCKED : QUIC_FRAME_STREAMS_BLOCKED_1; + Buffer[1] = (i & 1) ? 64 : 255; + Offset = 1; + ASSERT_FALSE(QuicStreamsBlockedFrameDecode((QUIC_FRAME_TYPE) Buffer[0], BufferLength, Buffer, &Offset, &DecodedFrame)); + } +} + INSTANTIATE_TEST_SUITE_P( FrameTest, StreamsBlockedFrameTest, @@ -319,6 +928,137 @@ TEST(FrameTest, NewConnectionIdFrameEncodeDecode) ASSERT_EQ(memcmp(Frame.Buffer, DecodedFrame.Buffer, 22), 0); } +struct NewConnectionIdFrameParams { + uint16_t BufferLength = 41; + uint8_t Buffer[41]; + + static auto GenerateDecodeFailParams() { + std::vector Params; + for (uint32_t i = 1; i < 16; ++i) { + NewConnectionIdFrameParams Frame; + uint32_t Index = 0; + uint16_t BufferLength = 0; + Frame.Buffer[Index] = QUIC_FRAME_NEW_CONNECTION_ID; + Index++; + BufferLength++; + + // + // Choose which fields to malform + // + if (i & 1) { + // + // Sequence number + // + Frame.Buffer[Index] = 255; + } else { + Frame.Buffer[Index] = 0; + } + Index++; + BufferLength++; + + if (i & 2) { + // + // Retire prior to + // + Frame.Buffer[Index] = 255; + } else { + Frame.Buffer[Index] = 0; + } + Index++; + BufferLength++; + + if (i & 4) { + // + // Connection ID. + // Length can be 0, 1, 20, or 21. + // When Length is 1 or 20, the buffer length should be adjusted to be too small. + // + uint16_t OldBufferLength = BufferLength; + uint32_t OldIndex = Index; + for (uint32_t j = 0; j < 4; ++j) { + BufferLength = OldBufferLength; + Index = OldIndex; + uint8_t CidLen; + if (j == 0) { + CidLen = 0; + BufferLength += 21; + } else if (j == 1) { + CidLen = 1; + // Don't increment BufferLength + } else if (j == 2) { + CidLen = 20; + BufferLength += 19; + } else { + CidLen = 21; + BufferLength += 21; + } + Frame.Buffer[Index] = CidLen; + Index++; + BufferLength++; + memset(&Frame.Buffer[Index], 127, CidLen); + Index += CidLen; + + // + // Stateless Reset token. + // Since the contents of the token aren't specified, don't put enough buffer to hold it. + // + uint8_t TokenLen; + if (i & 8) { + TokenLen = 8; + } else { + TokenLen = 16; + } + memset(&Frame.Buffer[Index], 65, TokenLen); + Index += TokenLen; + BufferLength += TokenLen; + + Frame.BufferLength = BufferLength; + Params.push_back(Frame); + } + } else { + // + // Connection ID is not altered, so use length of 1. + // + Frame.Buffer[Index] = 1; + Index++; + BufferLength++; + Frame.Buffer[Index] = 127; + Index++; + BufferLength++; + + // + // Stateless Reset token. + // Since the contents of the token aren't specified, don't put enough buffer to hold it. + // + uint8_t TokenLen; + if (i & 8) { + TokenLen = 8; + } else { + TokenLen = 16; + } + memset(&Frame.Buffer[Index], 65, TokenLen); + Index += TokenLen; + BufferLength += TokenLen; + + Frame.BufferLength = BufferLength; + Params.push_back(Frame); + } + } + + return Params; + } +}; + +struct NewConnectionIdFrameTest : ::testing::TestWithParam {}; + +TEST_P(NewConnectionIdFrameTest, DecodeNewConnectionIdFrameFail) { + QUIC_NEW_CONNECTION_ID_EX DecodedFrame; + uint16_t Offset = 1; + ASSERT_FALSE(QuicNewConnectionIDFrameDecode(GetParam().BufferLength, GetParam().Buffer, &Offset, &DecodedFrame)); +} + +INSTANTIATE_TEST_SUITE_P(FrameTest, NewConnectionIdFrameTest, ::testing::ValuesIn(NewConnectionIdFrameParams::GenerateDecodeFailParams())); + TEST(FrameTest, RetireConnectionIdFrameEncodeDecode) { QUIC_RETIRE_CONNECTION_ID_EX Frame = {63}; @@ -334,6 +1074,32 @@ TEST(FrameTest, RetireConnectionIdFrameEncodeDecode) ASSERT_EQ(Frame.Sequence, DecodedFrame.Sequence); } +struct RetireConnectionIdFrameParams { + uint8_t Buffer[2]; + uint16_t BufferLength = 2; + + static auto GenerateDecodeFailParams() { + std::vector Params; + for (uint32_t i = 0; i < 2; ++i) { + RetireConnectionIdFrameParams Temp; + Temp.Buffer[0] = QUIC_FRAME_RETIRE_CONNECTION_ID; + Temp.Buffer[1] = (i & 1) ? 64 : 255; + Params.push_back(Temp); + } + return Params; + } +}; + +struct RetireConnectionIdFrameTest : ::testing::TestWithParam {}; + +TEST_P(RetireConnectionIdFrameTest, DecodeRetireConnectionIdFrameFail) { + QUIC_RETIRE_CONNECTION_ID_EX DecodedFrame; + uint16_t Offset = 1; + ASSERT_FALSE(QuicRetireConnectionIDFrameDecode(GetParam().BufferLength, GetParam().Buffer, &Offset, &DecodedFrame)); +} + +INSTANTIATE_TEST_SUITE_P(FrameTest, RetireConnectionIdFrameTest, ::testing::ValuesIn(RetireConnectionIdFrameParams::GenerateDecodeFailParams())); + struct PathChallengeResponseFrameTest : ::testing::TestWithParam { }; @@ -357,6 +1123,17 @@ TEST_P(PathChallengeResponseFrameTest, PathChallengeResponseFrameEncodeDecode) ASSERT_EQ(memcmp(Frame.Data, DecodedFrame.Data, sizeof(Frame.Data)), 0); } +TEST_P(PathChallengeResponseFrameTest, DecodePathChallengeResponseFrameFail) { + const uint16_t BufferLength = 2; + QUIC_PATH_CHALLENGE_EX DecodedFrame; + uint8_t Buffer[BufferLength]; + uint16_t Offset = 1; + + Buffer[0] = (uint8_t) GetParam(); + Buffer[1] = 127; + ASSERT_FALSE(QuicPathChallengeFrameDecode(BufferLength, Buffer, &Offset, &DecodedFrame)); +} + INSTANTIATE_TEST_SUITE_P( FrameTest, PathChallengeResponseFrameTest, @@ -397,3 +1174,59 @@ INSTANTIATE_TEST_SUITE_P( else return "QUICClosed"; }); + +struct ConnectionCloseFrameParams { + uint8_t Buffer[5]; + uint16_t BufferLength; + + static auto GenerateDecodeFailParams() { + std::vector Params; + for (uint8_t Type : {QUIC_FRAME_CONNECTION_CLOSE, QUIC_FRAME_CONNECTION_CLOSE_1}) { + ConnectionCloseFrameParams Frame; + Frame.Buffer[0] = Type; + + for (int TestValue : {65, 255}) { + for (uint32_t i = 1; i < 4; ++i) { + Frame.Buffer[1] = (i & 1) ? TestValue : 0; + + if (Type == QUIC_FRAME_CONNECTION_CLOSE) { + for (uint32_t j = 0; j < 2; ++j) { + Frame.Buffer[2] = (j & 1) ? TestValue : 0; + + Frame.Buffer[3] = (i & 2) ? TestValue : 0; + if (Frame.Buffer[3] > 0) { + Frame.Buffer[4] = 'Z'; + } else { + Frame.Buffer[4] = 1; + } + + Frame.BufferLength = 5; + Params.push_back(Frame); + } + } else { + Frame.Buffer[2] = (i & 2) ? TestValue : 0; + if (Frame.Buffer[2] > 0) { + Frame.Buffer[3] = 'Z'; + } else { + Frame.Buffer[3] = 1; + } + + Frame.BufferLength = 4; + Params.push_back(Frame); + } + } + } + } + return Params; + } +}; + +struct ConnectionCloseFrameDecodeTest : ::testing::TestWithParam {}; + +TEST_P(ConnectionCloseFrameDecodeTest, DecodeConnectionCloseFrameFail) { + QUIC_CONNECTION_CLOSE_EX DecodedFrame; + uint16_t Offset = 1; + ASSERT_FALSE(QuicConnCloseFrameDecode((QUIC_FRAME_TYPE)GetParam().Buffer[0], GetParam().BufferLength, GetParam().Buffer, &Offset, &DecodedFrame)); +} + +INSTANTIATE_TEST_SUITE_P(FrameTest, ConnectionCloseFrameDecodeTest, ::testing::ValuesIn(ConnectionCloseFrameParams::GenerateDecodeFailParams()));