From a75413e35c1a877549457d6d3cc369bb1145c585 Mon Sep 17 00:00:00 2001 From: Chris Kennelly Date: Tue, 18 Aug 2026 21:45:29 -0700 Subject: [PATCH] Bolster fuzz coverage for HugeRegion. PiperOrigin-RevId: 966976986 --- tcmalloc/BUILD | 3 ++ tcmalloc/huge_region_fuzz.cc | 63 ++++++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/tcmalloc/BUILD b/tcmalloc/BUILD index 79261e33e..e7a3863af 100644 --- a/tcmalloc/BUILD +++ b/tcmalloc/BUILD @@ -780,6 +780,9 @@ cc_test( "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/log:check", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/strings:string_view", "@com_google_absl//absl/types:span", "@com_google_fuzztest//fuzztest", "@com_google_fuzztest//fuzztest:fuzztest_gtest_main", diff --git a/tcmalloc/huge_region_fuzz.cc b/tcmalloc/huge_region_fuzz.cc index 3df5c78a8..8efd79af5 100644 --- a/tcmalloc/huge_region_fuzz.cc +++ b/tcmalloc/huge_region_fuzz.cc @@ -27,6 +27,9 @@ #include "absl/base/attributes.h" #include "absl/container/flat_hash_set.h" #include "absl/log/check.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" +#include "absl/strings/string_view.h" #include "absl/types/span.h" #include "tcmalloc/huge_cache.h" #include "tcmalloc/huge_pages.h" @@ -128,6 +131,17 @@ struct Toggle { void Perform(State& state) const; }; +struct SetUnbackSuccess { + bool success; + + template + friend void AbslStringify(Sink& sink, const SetUnbackSuccess& s) { + absl::Format(&sink, "SetUnbackSuccess{.success=%v}", s.success); + } + + void Perform(State& state) const; +}; + struct Reentrant; struct GatherStatsPbtxt { @@ -148,8 +162,9 @@ struct PrintStats { void Perform(State& state) const; }; -using Instruction = std::variant; +using Instruction = + std::variant; struct Reentrant { std::vector subprogram; @@ -230,6 +245,13 @@ struct State { region.AddSpanStats(&small, &large); TC_CHECK_LE(region.free_backed(), region.backed()); TC_CHECK_LE(region.backed(), region.size()); + BackingStats stats = region.stats(); + EXPECT_EQ(stats.system_bytes, HugeRegion::size().in_bytes()); + EXPECT_EQ(stats.free_bytes, region.free_pages().in_bytes()); + EXPECT_EQ(stats.unmapped_bytes, region.unmapped_pages().in_bytes()); + EXPECT_EQ( + region.used_pages() + region.free_pages() + region.unmapped_pages(), + HugeRegion::size().in_pages()); } }; @@ -240,6 +262,8 @@ void Allocate::Perform(State& state) const { if (!state.region.MaybeGet(n, &p, &from_released)) { return; } + EXPECT_TRUE(state.region.contains(p)); + EXPECT_TRUE(state.region.contains(p + n - Length(1))); state.allocs.emplace_back(p, n); if (!from_released) { return; @@ -276,23 +300,44 @@ void Release::Perform(State& state) const { TC_CHECK_EQ(actual, NHugePages(0)); return; } - if (max_expected > NHugePages(0) && len > Length(0)) { - TC_CHECK_GT(actual, NHugePages(0)); - } - TC_CHECK_LE(actual, max_expected); + EXPECT_EQ(actual, max_expected); } void Stats::Perform(State& state) const { - state.region.stats(); SmallSpanStats small; LargeSpanStats large; state.region.AddSpanStats(&small, &large); + + Length small_normal_pages; + Length small_returned_pages; + for (size_t i = 0; i < kMaxPages.raw_num(); ++i) { + small_normal_pages += Length(i * small.normal_length[i]); + small_returned_pages += Length(i * small.returned_length[i]); + } + + EXPECT_EQ(small_normal_pages + large.normal_pages, state.region.free_pages()); + EXPECT_EQ(small_returned_pages + large.returned_pages, + state.region.unmapped_pages()); + + BackingStats stats = state.region.stats(); + EXPECT_EQ(stats.system_bytes, HugeRegion::size().in_bytes()); + EXPECT_EQ(stats.free_bytes, state.region.free_pages().in_bytes()); + EXPECT_EQ(stats.unmapped_bytes, state.region.unmapped_pages().in_bytes()); + EXPECT_EQ(state.region.used_pages() + state.region.free_pages() + + state.region.unmapped_pages(), + HugeRegion::size().in_pages()); + EXPECT_LE(state.region.free_backed(), state.region.backed()); + EXPECT_LE(state.region.backed(), state.region.size()); } void Toggle::Perform(State& state) const { state.unback.unback_success_ = !state.unback.unback_success_; } +void SetUnbackSuccess::Perform(State& state) const { + state.unback.unback_success_ = success; +} + void Reentrant::Perform(State& state) const { state.reentrant_stack.push_back(subprogram); } @@ -309,6 +354,7 @@ void GatherStatsPbtxt::Perform(State& state) const { void PrintStats::Perform(State& state) const { Printer p(&state.output[0], state.output.size()); state.region.Print(p); + ASSERT_LE(p.SpaceRequired(), state.output.size()); } void FuzzRegion(const std::vector& instructions, @@ -331,6 +377,9 @@ auto GetFlatInstructionDomain() { fuzztest::Arbitrary()), fuzztest::Map([](Toggle t) -> Instruction { return Instruction{t}; }, fuzztest::Arbitrary()), + fuzztest::Map( + [](SetUnbackSuccess s) -> Instruction { return Instruction{s}; }, + fuzztest::Arbitrary()), fuzztest::Map( [](GatherStatsPbtxt g) -> Instruction { return Instruction{g}; }, fuzztest::Arbitrary()),