Add property-based fuzz tests for QuicInlinedStringView PiperOrigin-RevId: 770154077
diff --git a/build/source_list.bzl b/build/source_list.bzl index 1c01d4a..0bbd9b2 100644 --- a/build/source_list.bzl +++ b/build/source_list.bzl
@@ -1316,6 +1316,7 @@ "quic/core/quic_framer_test.cc", "quic/core/quic_generic_session_test.cc", "quic/core/quic_idle_network_detector_test.cc", + "quic/core/quic_inlined_string_view_fuzz_test.cc", "quic/core/quic_inlined_string_view_test.cc", "quic/core/quic_interval_deque_test.cc", "quic/core/quic_interval_set_test.cc",
diff --git a/build/source_list.gni b/build/source_list.gni index 5ca530c..54bbd26 100644 --- a/build/source_list.gni +++ b/build/source_list.gni
@@ -1317,6 +1317,7 @@ "src/quiche/quic/core/quic_framer_test.cc", "src/quiche/quic/core/quic_generic_session_test.cc", "src/quiche/quic/core/quic_idle_network_detector_test.cc", + "src/quiche/quic/core/quic_inlined_string_view_fuzz_test.cc", "src/quiche/quic/core/quic_inlined_string_view_test.cc", "src/quiche/quic/core/quic_interval_deque_test.cc", "src/quiche/quic/core/quic_interval_set_test.cc",
diff --git a/build/source_list.json b/build/source_list.json index f35ce33..9830675 100644 --- a/build/source_list.json +++ b/build/source_list.json
@@ -1316,6 +1316,7 @@ "quiche/quic/core/quic_framer_test.cc", "quiche/quic/core/quic_generic_session_test.cc", "quiche/quic/core/quic_idle_network_detector_test.cc", + "quiche/quic/core/quic_inlined_string_view_fuzz_test.cc", "quiche/quic/core/quic_inlined_string_view_test.cc", "quiche/quic/core/quic_interval_deque_test.cc", "quiche/quic/core/quic_interval_set_test.cc",
diff --git a/quiche/quic/core/quic_inlined_string_view_fuzz_test.cc b/quiche/quic/core/quic_inlined_string_view_fuzz_test.cc new file mode 100644 index 0000000..d330853 --- /dev/null +++ b/quiche/quic/core/quic_inlined_string_view_fuzz_test.cc
@@ -0,0 +1,107 @@ +// Copyright 2025 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 <cstddef> +#include <string> + +#include "absl/strings/string_view.h" +#include "quiche/quic/core/quic_inlined_string_view.h" +#include "quiche/quic/core/quic_stream_send_buffer_inlining.h" +#include "quiche/common/platform/api/quiche_fuzztest.h" +#include "quiche/common/platform/api/quiche_test.h" + +namespace quic { +namespace { + +// Defines methods that test properties of `QuicInlinedStringView<kSize>`. +template <size_t kSize> +class Properties { + public: + static void AccessorsAreCorrect(absl::string_view view) { + QuicInlinedStringView<kSize> quic_view(view); + + // Copying the memory that `quic_view` points to enables ASAN to catch + // out-of-bounds accesses. + std::string copy(quic_view.view()); + + EXPECT_EQ(quic_view.empty(), view.empty()); + EXPECT_EQ(quic_view.size(), view.size()); + EXPECT_EQ(quic_view.view(), view); + EXPECT_EQ(quic_view.IsInlined(), view.size() < kSize); + + QuicInlinedStringView<kSize> quic_view_copy(quic_view); + EXPECT_EQ(quic_view_copy.empty(), view.empty()); + EXPECT_EQ(quic_view_copy.size(), view.size()); + EXPECT_EQ(quic_view_copy.view(), view); + EXPECT_EQ(quic_view_copy.IsInlined(), view.size() < kSize); + } + + static void IsEmptyAfterClear(absl::string_view view) { + QuicInlinedStringView<kSize> quic_view(view); + quic_view.clear(); + EXPECT_TRUE(quic_view.empty()); + } + + static void DifferentDataPointersWhenInlined(absl::string_view view) { + QuicInlinedStringView<kSize> quic_view(view); + if (!quic_view.empty()) { + const bool pointers_equal = view.data() == quic_view.data(); + EXPECT_EQ(!pointers_equal, quic_view.IsInlined()); + } else { + EXPECT_EQ(quic_view.data(), nullptr); + } + } +}; + +// Below, we fuzz each property with different values of `kSize`. +// - The value of 16 matches the `kSize` used by `BufferedSliceInlining`. +// - The value of 24 was chosen to demonstrate that these properties hold for at +// least one other value. +// - The value of 254 was chosen because it's the largest value supported by +// `QuicInlinedStringView`. +constexpr size_t kRealisticSize = + decltype(BufferedSliceInlining::slice)::kMaxInlinedSize + 1; +static_assert(kRealisticSize == 16); + +void NeverCrashes16(absl::string_view view) { + Properties<16>::AccessorsAreCorrect(view); +} +void NeverCrashes24(absl::string_view view) { + Properties<24>::AccessorsAreCorrect(view); +} +void NeverCrashes254(absl::string_view view) { + Properties<254>::AccessorsAreCorrect(view); +} +FUZZ_TEST(QuicInlinedStringViewTest, NeverCrashes16); +FUZZ_TEST(QuicInlinedStringViewTest, NeverCrashes24); +FUZZ_TEST(QuicInlinedStringViewTest, NeverCrashes254); + +void IsEmptyAfterClear16(absl::string_view view) { + Properties<16>::IsEmptyAfterClear(view); +} +void IsEmptyAfterClear24(absl::string_view view) { + Properties<24>::IsEmptyAfterClear(view); +} +void IsEmptyAfterClear254(absl::string_view view) { + Properties<254>::IsEmptyAfterClear(view); +} +FUZZ_TEST(QuicInlinedStringViewTest, IsEmptyAfterClear16); +FUZZ_TEST(QuicInlinedStringViewTest, IsEmptyAfterClear24); +FUZZ_TEST(QuicInlinedStringViewTest, IsEmptyAfterClear254); + +void DifferentDataPointersWhenInlined16(absl::string_view view) { + Properties<16>::DifferentDataPointersWhenInlined(view); +} +void DifferentDataPointersWhenInlined24(absl::string_view view) { + Properties<24>::DifferentDataPointersWhenInlined(view); +} +void DifferentDataPointersWhenInlined254(absl::string_view view) { + Properties<254>::DifferentDataPointersWhenInlined(view); +} +FUZZ_TEST(QuicInlinedStringViewTest, DifferentDataPointersWhenInlined16); +FUZZ_TEST(QuicInlinedStringViewTest, DifferentDataPointersWhenInlined24); +FUZZ_TEST(QuicInlinedStringViewTest, DifferentDataPointersWhenInlined254); + +} // namespace +} // namespace quic