[tint][utils] Make it more likely for fuzzers to decode containers
Instead of relying on a 'stop' byte to parse correctly, also treat an EOF as a successful decode.
Should increase the likelihood of the fuzzer decoding out-of-band data from WGSL comments.
Bug: tint:2223
Change-Id: Ife4fcf47c16d82cf50ff70649a5e1ed0d1c3dd93
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/185362
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
diff --git a/src/tint/utils/bytes/decoder.h b/src/tint/utils/bytes/decoder.h
index 606ce41..9f29bbf 100644
--- a/src/tint/utils/bytes/decoder.h
+++ b/src/tint/utils/bytes/decoder.h
@@ -133,7 +133,7 @@
static Result<std::unordered_map<K, V>> Decode(Reader& reader) {
std::unordered_map<K, V> out;
- while (true) {
+ while (!reader.IsEOF()) {
auto stop = bytes::Decode<bool>(reader);
if (stop != Success) {
return stop.Failure();
@@ -165,7 +165,7 @@
static Result<std::unordered_set<V>> Decode(Reader& reader) {
std::unordered_set<V> out;
- while (true) {
+ while (!reader.IsEOF()) {
auto stop = bytes::Decode<bool>(reader);
if (stop != Success) {
return stop.Failure();
@@ -193,7 +193,7 @@
static Result<std::vector<V>> Decode(Reader& reader) {
std::vector<V> out;
- while (true) {
+ while (!reader.IsEOF()) {
auto stop = bytes::Decode<bool>(reader);
if (stop != Success) {
return stop.Failure();
diff --git a/src/tint/utils/bytes/decoder_test.cc b/src/tint/utils/bytes/decoder_test.cc
index d272b0d..125d7e6 100644
--- a/src/tint/utils/bytes/decoder_test.cc
+++ b/src/tint/utils/bytes/decoder_test.cc
@@ -34,6 +34,7 @@
#include <utility>
#include "gmock/gmock.h"
+#include "src/tint/utils/result/result.h"
namespace tint {
namespace {
@@ -145,14 +146,21 @@
0x00, 0x70, 0x08, 0x80, //
0x01);
auto reader = BufferReader{Slice{data}};
- auto got = Decode<M>(reader);
- EXPECT_THAT(got.Get(), testing::ContainerEq(M{
- std::pair<uint8_t, uint16_t>(0x10u, 0x2002u),
- std::pair<uint8_t, uint16_t>(0x30u, 0x4004u),
- std::pair<uint8_t, uint16_t>(0x50u, 0x6006u),
- std::pair<uint8_t, uint16_t>(0x70u, 0x8008u),
- }));
- EXPECT_NE(Decode<M>(reader), Success);
+ {
+ auto got = Decode<M>(reader);
+ ASSERT_EQ(got, Success);
+ EXPECT_THAT(got.Get(), testing::ContainerEq(M{
+ std::pair<uint8_t, uint16_t>(0x10u, 0x2002u),
+ std::pair<uint8_t, uint16_t>(0x30u, 0x4004u),
+ std::pair<uint8_t, uint16_t>(0x50u, 0x6006u),
+ std::pair<uint8_t, uint16_t>(0x70u, 0x8008u),
+ }));
+ }
+ {
+ auto got = Decode<M>(reader);
+ ASSERT_EQ(got, Success);
+ EXPECT_THAT(got.Get(), testing::IsEmpty());
+ }
}
TEST(BytesDecoderTest, UnorderedSet) {
@@ -163,14 +171,21 @@
0x00, 0x08, 0x80, //
0x01);
auto reader = BufferReader{Slice{data}};
- auto got = Decode<S>(reader);
- EXPECT_THAT(got.Get(), testing::ContainerEq(S{
- 0x2002u,
- 0x4004u,
- 0x6006u,
- 0x8008u,
- }));
- EXPECT_NE(Decode<S>(reader), Success);
+ {
+ auto got = Decode<S>(reader);
+ ASSERT_EQ(got, Success);
+ EXPECT_THAT(got.Get(), testing::ContainerEq(S{
+ 0x2002u,
+ 0x4004u,
+ 0x6006u,
+ 0x8008u,
+ }));
+ }
+ {
+ auto got = Decode<S>(reader);
+ ASSERT_EQ(got, Success);
+ EXPECT_THAT(got.Get(), testing::IsEmpty());
+ }
}
TEST(BytesDecoderTest, Vector) {
@@ -181,14 +196,21 @@
0x00, 0x70, //
0x01);
auto reader = BufferReader{Slice{data}};
- auto got = Decode<M>(reader);
- EXPECT_THAT(got.Get(), testing::ContainerEq(M{
- 0x10u,
- 0x30u,
- 0x50u,
- 0x70u,
- }));
- EXPECT_NE(Decode<M>(reader), Success);
+ {
+ auto got = Decode<M>(reader);
+ ASSERT_EQ(got, Success);
+ EXPECT_THAT(got.Get(), testing::ContainerEq(M{
+ 0x10u,
+ 0x30u,
+ 0x50u,
+ 0x70u,
+ }));
+ }
+ {
+ auto got = Decode<M>(reader);
+ ASSERT_EQ(got, Success);
+ EXPECT_THAT(got.Get(), testing::IsEmpty());
+ }
}
TEST(BytesDecoderTest, Optional) {
diff --git a/src/tint/utils/bytes/reader.cc b/src/tint/utils/bytes/reader.cc
index 9c28d1d..fc2dfc2 100644
--- a/src/tint/utils/bytes/reader.cc
+++ b/src/tint/utils/bytes/reader.cc
@@ -41,4 +41,8 @@
return n;
}
+bool BufferReader::IsEOF() const {
+ return bytes_remaining_ == 0;
+}
+
} // namespace tint::bytes
diff --git a/src/tint/utils/bytes/reader.h b/src/tint/utils/bytes/reader.h
index 59e1864..3b5f51a 100644
--- a/src/tint/utils/bytes/reader.h
+++ b/src/tint/utils/bytes/reader.h
@@ -54,6 +54,9 @@
/// then the end of the stream has been reached.
virtual size_t Read(std::byte* out, size_t count) = 0;
+ /// @returns true if the Reader has no more bytes to read.
+ virtual bool IsEOF() const = 0;
+
/// Reads an integer from the stream, performing byte swapping if the stream's endianness
/// differs from the native endianness.
/// If there are too few bytes remaining in the stream, then a failure is returned.
@@ -140,6 +143,9 @@
/// @copydoc Reader::Read
size_t Read(std::byte* out, size_t count) override;
+ /// @copydoc Reader::IsEOF
+ bool IsEOF() const override;
+
private:
/// The data to read from
const std::byte* data_ = nullptr;
diff --git a/src/tint/utils/bytes/reader_test.cc b/src/tint/utils/bytes/reader_test.cc
index 04ad6af..2e90e88 100644
--- a/src/tint/utils/bytes/reader_test.cc
+++ b/src/tint/utils/bytes/reader_test.cc
@@ -39,47 +39,87 @@
TEST(BufferReaderTest, IntegerBigEndian) {
auto data = Data(0x10, 0x20, 0x30, 0x40);
- auto u32 = BufferReader{Slice{data}}.Int<uint32_t>(Endianness::kBig);
+
+ BufferReader u32_reader{Slice{data}};
+ EXPECT_FALSE(u32_reader.IsEOF());
+ auto u32 = u32_reader.Int<uint32_t>(Endianness::kBig);
EXPECT_EQ(u32, 0x10203040u);
- auto i32 = BufferReader{Slice{data}}.Int<int32_t>(Endianness::kBig);
+ EXPECT_TRUE(u32_reader.IsEOF());
+
+ BufferReader i32_reader{Slice{data}};
+ EXPECT_FALSE(i32_reader.IsEOF());
+ auto i32 = i32_reader.Int<int32_t>(Endianness::kBig);
EXPECT_EQ(i32, 0x10203040);
+ EXPECT_TRUE(i32_reader.IsEOF());
}
TEST(BufferReaderTest, IntegerBigEndian_TooShort) {
auto data = Data(0x10, 0x20);
- auto u32 = BufferReader{Slice{data}}.Int<uint32_t>(Endianness::kBig);
+
+ BufferReader u32_reader{Slice{data}};
+ EXPECT_FALSE(u32_reader.IsEOF());
+ auto u32 = u32_reader.Int<uint32_t>(Endianness::kBig);
EXPECT_NE(u32, Success);
- auto i32 = BufferReader{Slice{data}}.Int<int32_t>(Endianness::kBig);
+ EXPECT_TRUE(u32_reader.IsEOF());
+
+ BufferReader i32_reader{Slice{data}};
+ EXPECT_FALSE(i32_reader.IsEOF());
+ auto i32 = i32_reader.Int<int32_t>(Endianness::kBig);
EXPECT_NE(i32, Success);
+ EXPECT_TRUE(i32_reader.IsEOF());
}
TEST(BufferReaderTest, IntegerLittleEndian) {
auto data = Data(0x10, 0x20, 0x30, 0x40);
- auto u32 = BufferReader{Slice{data}}.Int<uint32_t>(Endianness::kLittle);
+
+ BufferReader u32_reader{Slice{data}};
+ EXPECT_FALSE(u32_reader.IsEOF());
+ auto u32 = u32_reader.Int<uint32_t>(Endianness::kLittle);
EXPECT_EQ(u32, 0x40302010u);
- auto i32 = BufferReader{Slice{data}}.Int<int32_t>(Endianness::kLittle);
+ EXPECT_TRUE(u32_reader.IsEOF());
+
+ BufferReader i32_reader{Slice{data}};
+ EXPECT_FALSE(i32_reader.IsEOF());
+ auto i32 = i32_reader.Int<int32_t>(Endianness::kLittle);
EXPECT_EQ(i32, 0x40302010);
+ EXPECT_TRUE(i32_reader.IsEOF());
}
TEST(BufferReaderTest, IntegerLittleEndian_TooShort) {
auto data = Data(0x30, 0x40);
- auto u32 = BufferReader{Slice{data}}.Int<uint32_t>(Endianness::kLittle);
+
+ BufferReader u32_reader{Slice{data}};
+ EXPECT_FALSE(u32_reader.IsEOF());
+ auto u32 = u32_reader.Int<uint32_t>(Endianness::kLittle);
EXPECT_NE(u32, Success);
- auto i32 = BufferReader{Slice{data}}.Int<int32_t>(Endianness::kLittle);
+ EXPECT_TRUE(u32_reader.IsEOF());
+
+ BufferReader i32_reader{Slice{data}};
+ EXPECT_FALSE(i32_reader.IsEOF());
+ auto i32 = i32_reader.Int<int32_t>(Endianness::kLittle);
EXPECT_NE(i32, Success);
+ EXPECT_TRUE(i32_reader.IsEOF());
}
TEST(BufferReaderTest, Float) {
auto data = Data(0x00, 0x00, 0x08, 0x41);
- auto f32 = BufferReader{Slice{data}}.Float<float>();
+
+ BufferReader f32_reader{Slice{data}};
+ EXPECT_FALSE(f32_reader.IsEOF());
+ auto f32 = f32_reader.Float<float>();
ASSERT_EQ(f32, Success);
EXPECT_EQ(f32.Get(), 8.5f);
+ EXPECT_TRUE(f32_reader.IsEOF());
}
TEST(BufferReaderTest, Float_TooShort) {
auto data = Data(0x08, 0x41);
- auto f32 = BufferReader{Slice{data}}.Float<float>();
+
+ BufferReader f32_reader{Slice{data}};
+ EXPECT_FALSE(f32_reader.IsEOF());
+ auto f32 = f32_reader.Float<float>();
EXPECT_NE(f32, Success);
+ EXPECT_TRUE(f32_reader.IsEOF());
}
} // namespace