| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "quiche/http2/decoder/decode_buffer.h" |
| |
| #include <functional> |
| |
| #include "quiche/http2/test_tools/http2_random.h" |
| #include "quiche/common/platform/api/quiche_logging.h" |
| #include "quiche/common/platform/api/quiche_test.h" |
| |
| namespace http2 { |
| namespace test { |
| namespace { |
| |
| enum class TestEnumClass32 { |
| kValue1 = 1, |
| kValue99 = 99, |
| kValue1M = 1000000, |
| }; |
| |
| enum class TestEnumClass8 { |
| kValue1 = 1, |
| kValue2 = 1, |
| kValue99 = 99, |
| kValue255 = 255, |
| }; |
| |
| enum TestEnum8 { |
| kMaskLo = 0x01, |
| kMaskHi = 0x80, |
| }; |
| |
| struct TestStruct { |
| uint8_t f1; |
| uint16_t f2; |
| uint32_t f3; // Decoded as a uint24 |
| uint32_t f4; |
| uint32_t f5; // Decoded as if uint31 |
| TestEnumClass32 f6; |
| TestEnumClass8 f7; |
| TestEnum8 f8; |
| }; |
| |
| class DecodeBufferTest : public quiche::test::QuicheTest { |
| protected: |
| Http2Random random_; |
| uint32_t decode_offset_; |
| }; |
| |
| TEST_F(DecodeBufferTest, DecodesFixedInts) { |
| const char data[] = "\x01\x12\x23\x34\x45\x56\x67\x78\x89\x9a"; |
| DecodeBuffer b1(data, strlen(data)); |
| EXPECT_EQ(1, b1.DecodeUInt8()); |
| EXPECT_EQ(0x1223u, b1.DecodeUInt16()); |
| EXPECT_EQ(0x344556u, b1.DecodeUInt24()); |
| EXPECT_EQ(0x6778899Au, b1.DecodeUInt32()); |
| } |
| |
| // Make sure that DecodeBuffer is not copying input, just pointing into |
| // provided input buffer. |
| TEST_F(DecodeBufferTest, HasNotCopiedInput) { |
| const char data[] = "ab"; |
| DecodeBuffer b1(data, 2); |
| |
| EXPECT_EQ(2u, b1.Remaining()); |
| EXPECT_EQ(0u, b1.Offset()); |
| EXPECT_FALSE(b1.Empty()); |
| EXPECT_EQ(data, b1.cursor()); // cursor points to input buffer |
| EXPECT_TRUE(b1.HasData()); |
| |
| b1.AdvanceCursor(1); |
| |
| EXPECT_EQ(1u, b1.Remaining()); |
| EXPECT_EQ(1u, b1.Offset()); |
| EXPECT_FALSE(b1.Empty()); |
| EXPECT_EQ(&data[1], b1.cursor()); |
| EXPECT_TRUE(b1.HasData()); |
| |
| b1.AdvanceCursor(1); |
| |
| EXPECT_EQ(0u, b1.Remaining()); |
| EXPECT_EQ(2u, b1.Offset()); |
| EXPECT_TRUE(b1.Empty()); |
| EXPECT_EQ(&data[2], b1.cursor()); |
| EXPECT_FALSE(b1.HasData()); |
| |
| DecodeBuffer b2(data, 0); |
| |
| EXPECT_EQ(0u, b2.Remaining()); |
| EXPECT_EQ(0u, b2.Offset()); |
| EXPECT_TRUE(b2.Empty()); |
| EXPECT_EQ(data, b2.cursor()); |
| EXPECT_FALSE(b2.HasData()); |
| } |
| |
| // DecodeBufferSubset can't go beyond the end of the base buffer. |
| TEST_F(DecodeBufferTest, DecodeBufferSubsetLimited) { |
| const char data[] = "abc"; |
| DecodeBuffer base(data, 3); |
| base.AdvanceCursor(1); |
| DecodeBufferSubset subset(&base, 100); |
| EXPECT_EQ(2u, subset.FullSize()); |
| } |
| |
| // DecodeBufferSubset advances the cursor of its base upon destruction. |
| TEST_F(DecodeBufferTest, DecodeBufferSubsetAdvancesCursor) { |
| const char data[] = "abc"; |
| const size_t size = sizeof(data) - 1; |
| EXPECT_EQ(3u, size); |
| DecodeBuffer base(data, size); |
| { |
| // First no change to the cursor. |
| DecodeBufferSubset subset(&base, size + 100); |
| EXPECT_EQ(size, subset.FullSize()); |
| EXPECT_EQ(base.FullSize(), subset.FullSize()); |
| EXPECT_EQ(0u, subset.Offset()); |
| } |
| EXPECT_EQ(0u, base.Offset()); |
| EXPECT_EQ(size, base.Remaining()); |
| } |
| |
| // Make sure that DecodeBuffer ctor complains about bad args. |
| #if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) |
| TEST(DecodeBufferDeathTest, NonNullBufferRequired) { |
| EXPECT_QUICHE_DEBUG_DEATH({ DecodeBuffer b(nullptr, 3); }, "nullptr"); |
| } |
| |
| // Make sure that DecodeBuffer ctor complains about bad args. |
| TEST(DecodeBufferDeathTest, ModestBufferSizeRequired) { |
| EXPECT_QUICHE_DEBUG_DEATH( |
| { |
| constexpr size_t kLength = DecodeBuffer::kMaxDecodeBufferLength + 1; |
| auto data = std::make_unique<char[]>(kLength); |
| DecodeBuffer b(data.get(), kLength); |
| }, |
| "Max.*Length"); |
| } |
| |
| // Make sure that DecodeBuffer detects advance beyond end, in debug mode. |
| TEST(DecodeBufferDeathTest, LimitedAdvance) { |
| { |
| // Advance right up to end is OK. |
| const char data[] = "abc"; |
| DecodeBuffer b(data, 3); |
| b.AdvanceCursor(3); // OK |
| EXPECT_TRUE(b.Empty()); |
| } |
| EXPECT_QUICHE_DEBUG_DEATH( |
| { |
| // Going beyond is not OK. |
| const char data[] = "abc"; |
| DecodeBuffer b(data, 3); |
| b.AdvanceCursor(4); |
| }, |
| "Remaining"); |
| } |
| |
| // Make sure that DecodeBuffer detects decode beyond end, in debug mode. |
| TEST(DecodeBufferDeathTest, DecodeUInt8PastEnd) { |
| const char data[] = {0x12, 0x23}; |
| DecodeBuffer b(data, sizeof data); |
| EXPECT_EQ(2u, b.FullSize()); |
| EXPECT_EQ(0x1223, b.DecodeUInt16()); |
| EXPECT_QUICHE_DEBUG_DEATH({ b.DecodeUInt8(); }, "Remaining"); |
| } |
| |
| // Make sure that DecodeBuffer detects decode beyond end, in debug mode. |
| TEST(DecodeBufferDeathTest, DecodeUInt16OverEnd) { |
| const char data[] = {0x12, 0x23, 0x34}; |
| DecodeBuffer b(data, sizeof data); |
| EXPECT_EQ(3u, b.FullSize()); |
| EXPECT_EQ(0x1223, b.DecodeUInt16()); |
| EXPECT_QUICHE_DEBUG_DEATH({ b.DecodeUInt16(); }, "Remaining"); |
| } |
| |
| // Make sure that DecodeBuffer doesn't agree with having two subsets. |
| TEST(DecodeBufferSubsetDeathTest, TwoSubsets) { |
| const char data[] = "abc"; |
| DecodeBuffer base(data, 3); |
| DecodeBufferSubset subset1(&base, 1); |
| EXPECT_QUICHE_DEBUG_DEATH({ DecodeBufferSubset subset2(&base, 1); }, |
| "There is already a subset"); |
| } |
| |
| // Make sure that DecodeBufferSubset notices when the base's cursor has moved. |
| TEST(DecodeBufferSubsetDeathTest, BaseCursorAdvanced) { |
| const char data[] = "abc"; |
| DecodeBuffer base(data, 3); |
| base.AdvanceCursor(1); |
| EXPECT_QUICHE_DEBUG_DEATH( |
| { |
| DecodeBufferSubset subset1(&base, 2); |
| base.AdvanceCursor(1); |
| }, |
| "Access via subset only when present"); |
| } |
| #endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) |
| |
| } // namespace |
| } // namespace test |
| } // namespace http2 |