|
|
|
// 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 <string.h>
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#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:
|
|
|
|
// explicit 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<int> 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<uintptr_t>(str_data),
|
|
|
|
// reinterpret_cast<uintptr_t>(data_.data()));
|
|
|
|
// EXPECT_LT(reinterpret_cast<uintptr_t>(str_data),
|
|
|
|
// reinterpret_cast<uintptr_t>(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<EpsStream*>(e);
|
|
|
|
// if (!old_end) stream->error_ = true;
|
|
|
|
// return new_start;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// upb_EpsCopyInputStream eps_;
|
|
|
|
// std::string data_;
|
|
|
|
// const char* ptr_;
|
|
|
|
// std::vector<int> 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<ReadOp, ReadStringOp, PushLimitOp> Op;
|
|
|
|
//
|
|
|
|
// struct EpsCopyTestScript {
|
|
|
|
// int data_size;
|
|
|
|
// bool enable_aliasing;
|
|
|
|
// std::vector<Op> 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<EpsCopyTestScript>(
|
|
|
|
// InRange(0, max_data_size), // data_size
|
|
|
|
// Arbitrary<bool>(), // enable_aliasing
|
|
|
|
// VectorOf(VariantOf(
|
|
|
|
// // ReadOp
|
|
|
|
// StructOf<ReadOp>(InRange(0, kUpb_EpsCopyInputStream_SlopBytes)),
|
|
|
|
// // ReadStringOp
|
|
|
|
// StructOf<ReadStringOp>(NonNegative<int>()),
|
|
|
|
// // PushLimitOp
|
|
|
|
// StructOf<PushLimitOp>(NonNegative<int>()))));
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// // 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<char>(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<ReadOp>(&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<ReadStringOp>(&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<PushLimitOp>(&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
|