// Protocol Buffers - Google's data interchange format // Copyright 2023 Google LLC. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd #include "upb/wire/eps_copy_input_stream.h" #include #include #include #include "upb/mem/arena.hpp" // begin:google_only // #include "testing/fuzzing/fuzztest.h" // end:google_only namespace { TEST(EpsCopyInputStreamTest, ZeroSize) { upb_EpsCopyInputStream stream; const char* ptr = nullptr; upb_EpsCopyInputStream_Init(&stream, &ptr, 0, false); EXPECT_TRUE( upb_EpsCopyInputStream_IsDoneWithCallback(&stream, &ptr, nullptr)); } // begin:google_only // // // We create a simple, trivial implementation of the stream that we can test // // our real implementation against. // // class FakeStream { // public: // FakeStream(const std::string& data) : data_(data), offset_(0) { // limits_.push_back(data.size()); // } // // // If we reached one or more limits correctly, returns the number of limits // // ended. If we tried to read beyond the current limit, returns -1. // // Otherwise, for simple success, returns 0. // int ReadData(int n, std::string* data) { // if (n > BytesUntilLimit()) return -1; // // data->assign(data_.data() + offset_, n); // offset_ += n; // // int end_limit_count = 0; // // while (BytesUntilLimit() == 0) { // if (PopLimit()) { // end_limit_count++; // } else { // eof_ = true; // break; // } // } // // return end_limit_count; // } // // bool TryPushLimit(int limit) { // if (!CheckSize(limit)) return false; // limits_.push_back(offset_ + limit); // return true; // } // // bool IsEof() const { return eof_; } // // private: // int BytesUntilLimit() const { return limits_.back() - offset_; } // bool CheckSize(int size) const { return BytesUntilLimit() >= size; } // // // Return false on EOF. // bool PopLimit() { // limits_.pop_back(); // return !limits_.empty(); // } // // std::string data_; // // Limits, specified in absolute stream terms. // std::vector limits_; // int offset_; // bool eof_ = false; // }; // // char tmp_buf[kUpb_EpsCopyInputStream_SlopBytes]; // // class EpsStream { // public: // EpsStream(const std::string& data, bool enable_aliasing) // : data_(data), enable_aliasing_(enable_aliasing) { // ptr_ = data_.data(); // upb_EpsCopyInputStream_Init(&eps_, &ptr_, data_.size(), enable_aliasing); // } // // // Returns false at EOF or error. // int ReadData(int n, std::string* data) { // EXPECT_LE(n, kUpb_EpsCopyInputStream_SlopBytes); // if (enable_aliasing_) { // EXPECT_TRUE(upb_EpsCopyInputStream_AliasingAvailable(&eps_, ptr_, n)); // } // // We want to verify that we can read kUpb_EpsCopyInputStream_SlopBytes // // safely, even if we haven't actually been requested to read that much. // // We copy to a global buffer so the copy can't be optimized away. // memcpy(&tmp_buf, ptr_, kUpb_EpsCopyInputStream_SlopBytes); // data->assign(tmp_buf, n); // ptr_ += n; // if (enable_aliasing_) { // EXPECT_TRUE(upb_EpsCopyInputStream_AliasingAvailable(&eps_, ptr_, 0)); // } // return PopLimits(); // } // // int ReadString(int n, std::string* data) { // if (!upb_EpsCopyInputStream_CheckSize(&eps_, ptr_, n)) return -1; // const char* str_data = ptr_; // if (enable_aliasing_) { // EXPECT_TRUE(upb_EpsCopyInputStream_AliasingAvailable(&eps_, ptr_, n)); // } // ptr_ = upb_EpsCopyInputStream_ReadString(&eps_, &str_data, n, arena_.ptr()); // if (!ptr_) return -1; // if (enable_aliasing_ && n) { // EXPECT_GE(reinterpret_cast(str_data), // reinterpret_cast(data_.data())); // EXPECT_LT(reinterpret_cast(str_data), // reinterpret_cast(data_.data() + data_.size())); // EXPECT_TRUE(upb_EpsCopyInputStream_AliasingAvailable(&eps_, ptr_, 0)); // } // data->assign(str_data, n); // return PopLimits(); // } // // bool TryPushLimit(int limit) { // if (!upb_EpsCopyInputStream_CheckSize(&eps_, ptr_, limit)) return false; // deltas_.push_back(upb_EpsCopyInputStream_PushLimit(&eps_, ptr_, limit)); // return true; // } // // bool IsEof() const { return eof_; } // // private: // int PopLimits() { // int end_limit_count = 0; // // while (IsAtLimit()) { // if (error_) return -1; // if (PopLimit()) { // end_limit_count++; // } else { // eof_ = true; // EOF. // break; // } // } // // return error_ ? -1 : end_limit_count; // } // // bool IsAtLimit() { // return upb_EpsCopyInputStream_IsDoneWithCallback( // &eps_, &ptr_, &EpsStream::IsDoneFallback); // } // // // Return false on EOF. // bool PopLimit() { // if (deltas_.empty()) return false; // upb_EpsCopyInputStream_PopLimit(&eps_, ptr_, deltas_.back()); // deltas_.pop_back(); // return true; // } // // static const char* IsDoneFallback(upb_EpsCopyInputStream* e, const char* ptr, // int overrun) { // return _upb_EpsCopyInputStream_IsDoneFallbackInline( // e, ptr, overrun, &EpsStream::BufferFlipCallback); // } // // static const char* BufferFlipCallback(upb_EpsCopyInputStream* e, // const char* old_end, // const char* new_start) { // EpsStream* stream = reinterpret_cast(e); // if (!old_end) stream->error_ = true; // return new_start; // } // // upb_EpsCopyInputStream eps_; // std::string data_; // const char* ptr_; // std::vector deltas_; // upb::Arena arena_; // bool error_ = false; // bool eof_ = false; // bool enable_aliasing_; // }; // // // Reads N bytes from the given position. // struct ReadOp { // int bytes; // Must be <= kUpb_EpsCopyInputStream_SlopBytes. // }; // // struct ReadStringOp { // int bytes; // }; // // // Pushes a new limit of N bytes from the current position. // struct PushLimitOp { // int bytes; // }; // // typedef std::variant Op; // // struct EpsCopyTestScript { // int data_size; // bool enable_aliasing; // std::vector ops; // }; // // auto ArbitraryEpsCopyTestScript() { // using ::fuzztest::Arbitrary; // using ::fuzztest::InRange; // using ::fuzztest::NonNegative; // using ::fuzztest::StructOf; // using ::fuzztest::VariantOf; // using ::fuzztest::VectorOf; // // int max_data_size = 512; // // return StructOf( // InRange(0, max_data_size), // data_size // Arbitrary(), // enable_aliasing // VectorOf(VariantOf( // // ReadOp // StructOf(InRange(0, kUpb_EpsCopyInputStream_SlopBytes)), // // ReadStringOp // StructOf(NonNegative()), // // PushLimitOp // StructOf(NonNegative())))); // } // // // Run a test that creates both real stream and a fake stream, and validates // // that they have the same behavior. // void TestAgainstFakeStream(const EpsCopyTestScript& script) { // std::string data(script.data_size, 'x'); // for (int i = 0; i < script.data_size; ++i) { // data[i] = static_cast(i & 0xff); // } // // FakeStream fake_stream(data); // EpsStream eps_stream(data, script.enable_aliasing); // // for (const auto& op : script.ops) { // if (const ReadOp* read_op = std::get_if(&op)) { // std::string data_fake; // std::string data_eps; // int fake_result = fake_stream.ReadData(read_op->bytes, &data_fake); // int eps_result = eps_stream.ReadData(read_op->bytes, &data_eps); // EXPECT_EQ(fake_result, eps_result); // if (fake_result == -1) break; // Error // EXPECT_EQ(data_fake, data_eps); // EXPECT_EQ(fake_stream.IsEof(), eps_stream.IsEof()); // if (fake_stream.IsEof()) break; // } else if (const ReadStringOp* read_op = std::get_if(&op)) { // std::string data_fake; // std::string data_eps; // int fake_result = fake_stream.ReadData(read_op->bytes, &data_fake); // int eps_result = eps_stream.ReadString(read_op->bytes, &data_eps); // EXPECT_EQ(fake_result, eps_result); // if (fake_result == -1) break; // Error // EXPECT_EQ(data_fake, data_eps); // EXPECT_EQ(fake_stream.IsEof(), eps_stream.IsEof()); // if (fake_stream.IsEof()) break; // } else if (const PushLimitOp* push = std::get_if(&op)) { // EXPECT_EQ(fake_stream.TryPushLimit(push->bytes), // eps_stream.TryPushLimit(push->bytes)); // } else { // EXPECT_TRUE(false); // Unknown op. // } // } // } // // // Test with: // // $ blaze run --config=fuzztest third_party/upb:eps_copy_input_stream_test \ // // -- --gunit_fuzz= // FUZZ_TEST(EpsCopyFuzzTest, TestAgainstFakeStream) // .WithDomains(ArbitraryEpsCopyTestScript()); // // TEST(EpsCopyFuzzTest, TestAgainstFakeStreamRegression) { // TestAgainstFakeStream({299, // false, // { // PushLimitOp{2}, // ReadOp{14}, // }}); // } // // TEST(EpsCopyFuzzTest, AliasingEnabledZeroSizeReadString) { // TestAgainstFakeStream({510, true, {ReadStringOp{0}}}); // } // // TEST(EpsCopyFuzzTest, AliasingDisabledZeroSizeReadString) { // TestAgainstFakeStream({510, false, {ReadStringOp{0}}}); // } // // TEST(EpsCopyFuzzTest, ReadStringZero) { // TestAgainstFakeStream({0, true, {ReadStringOp{0}}}); // } // // TEST(EpsCopyFuzzTest, ReadZero) { // TestAgainstFakeStream({0, true, {ReadOp{0}}}); // } // // TEST(EpsCopyFuzzTest, ReadZeroTwice) { // TestAgainstFakeStream({0, true, {ReadOp{0}, ReadOp{0}}}); // } // // TEST(EpsCopyFuzzTest, ReadStringZeroThenRead) { // TestAgainstFakeStream({0, true, {ReadStringOp{0}, ReadOp{0}}}); // } // // TEST(EpsCopyFuzzTest, ReadStringOverflowsBufferButNotLimit) { // TestAgainstFakeStream({351, // false, // { // ReadOp{7}, // PushLimitOp{2147483647}, // ReadStringOp{344}, // }}); // } // // TEST(EpsCopyFuzzTest, LastBufferAliasing) { // TestAgainstFakeStream({27, true, {ReadOp{12}, ReadStringOp{3}}}); // } // // TEST(EpsCopyFuzzTest, FirstBufferAliasing) { // TestAgainstFakeStream({7, true, {ReadStringOp{3}}}); // } // // end:google_only } // namespace