Make the maximum input size for LLVMFuzzer targets configurable. Replace the hardcoded maximum input size (4096) with a new flag, `--llvm_fuzzer_wrapper_max_input_size`, allowing users to specify a different limit for wrapped LLVMFuzzer targets. PiperOrigin-RevId: 878511344
diff --git a/fuzztest/BUILD b/fuzztest/BUILD index c3bd226..fe241cd 100644 --- a/fuzztest/BUILD +++ b/fuzztest/BUILD
@@ -171,6 +171,7 @@ "@com_google_fuzztest//common:logging", "@com_google_fuzztest//fuzztest/internal:io", "@com_google_fuzztest//fuzztest/internal/domains:core_domains_impl", + "@com_google_fuzztest//fuzztest/internal/domains:lazy", ], alwayslink = True, )
diff --git a/fuzztest/CMakeLists.txt b/fuzztest/CMakeLists.txt index 960b6e2..ecaf619 100644 --- a/fuzztest/CMakeLists.txt +++ b/fuzztest/CMakeLists.txt
@@ -235,4 +235,5 @@ fuzztest::common_logging fuzztest::io fuzztest::core_domains_impl + fuzztest::lazy )
diff --git a/fuzztest/internal/domains/BUILD b/fuzztest/internal/domains/BUILD index 8434788..58a6a81 100644 --- a/fuzztest/internal/domains/BUILD +++ b/fuzztest/internal/domains/BUILD
@@ -235,3 +235,16 @@ hdrs = ["utf.h"], deps = ["@abseil-cpp//absl/strings:string_view"], ) + +cc_library( + name = "lazy", + hdrs = ["lazy.h"], + deps = [ + ":core_domains_impl", + "@abseil-cpp//absl/random:bit_gen_ref", + "@abseil-cpp//absl/status", + "@com_google_fuzztest//common:logging", + "@com_google_fuzztest//fuzztest/internal:meta", + "@com_google_fuzztest//fuzztest/internal:serialization", + ], +)
diff --git a/fuzztest/internal/domains/CMakeLists.txt b/fuzztest/internal/domains/CMakeLists.txt index 6e27f15..3c1759b 100644 --- a/fuzztest/internal/domains/CMakeLists.txt +++ b/fuzztest/internal/domains/CMakeLists.txt
@@ -224,3 +224,16 @@ DEPS absl::string_view ) +fuzztest_cc_library( + NAME + lazy + HDRS + "lazy.h" + DEPS + fuzztest::core_domains_impl + absl::random_bit_gen_ref + absl::status + fuzztest::common_logging + fuzztest::meta + fuzztest::serialization +)
diff --git a/fuzztest/internal/domains/lazy.h b/fuzztest/internal/domains/lazy.h new file mode 100644 index 0000000..ff9fd7e --- /dev/null +++ b/fuzztest/internal/domains/lazy.h
@@ -0,0 +1,130 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_LAZY_H_ +#define FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_LAZY_H_ + +#include <cstddef> +#include <functional> +#include <memory> +#include <optional> +#include <tuple> +#include <utility> + +#include "absl/random/bit_gen_ref.h" +#include "absl/status/status.h" +#include "./common/logging.h" +#include "./fuzztest/internal/domains/domain_base.h" +#include "./fuzztest/internal/meta.h" +#include "./fuzztest/internal/serialization.h" + +namespace fuzztest { +namespace internal { + +// A helper domain that allows its inner domain to be lazily initialized. +template <typename DomainT, typename... Args> +class Lazy : public domain_implementor::DomainBase<Lazy<DomainT, Args...>, + value_type_t<DomainT>, + corpus_type_t<DomainT>> { + public: + using typename Lazy::DomainBase::corpus_type; + using typename Lazy::DomainBase::value_type; + + Lazy(Args&&... args) + : args_(std::tuple<Args...>(std::forward<Args>(args)...)) {} + + Lazy(const Lazy& other) { + if (other.inner_ != nullptr) { + inner_ = std::make_unique<DomainT>(*other.inner_); + } else { + args_ = other.args_; + setup_ = other.setup_; + } + } + + Lazy(Lazy&& other) noexcept = default; + Lazy& operator=(Lazy&& other) = default; + + corpus_type Init(absl::BitGenRef prng) { return GetInnerDomain().Init(prng); } + + Lazy& WithLazySetup(std::function<void(DomainT&)> setup) { + setup_ = std::move(setup); + return *this; + } + + void Mutate(corpus_type& corpus_value, absl::BitGenRef prng, + const domain_implementor::MutationMetadata& metadata, + bool only_shrink) { + GetInnerDomain().Mutate(corpus_value, prng, metadata, only_shrink); + } + + value_type GetValue(const corpus_type& corpus_value) const { + return GetInnerDomain().GetValue(corpus_value); + } + + std::optional<corpus_type> FromValue(const value_type& v) const { + return GetInnerDomain().FromValue(v); + } + + std::optional<corpus_type> ParseCorpus(const IRObject& obj) const { + return GetInnerDomain().ParseCorpus(obj); + } + + IRObject SerializeCorpus(const corpus_type& corpus_value) const { + return GetInnerDomain().SerializeCorpus(corpus_value); + } + + absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { + return GetInnerDomain().ValidateCorpusValue(corpus_value); + } + + auto GetPrinter() const { return GetInnerDomain().GetPrinter(); } + + private: + template <std::size_t... Is> + void PopulateInner(std::index_sequence<Is...>) const { + FUZZTEST_CHECK(args_.has_value()) + << "args is unavailable for creating the inner domain"; + inner_ = std::make_unique<DomainT>(std::move(std::get<Is>(*args_))...); + if (setup_) setup_(*inner_); + args_ = std::nullopt; + setup_ = {}; + } + + const DomainT& GetInnerDomain() const { + if (inner_ == nullptr) { + PopulateInner(std::index_sequence_for<Args...>{}); + } + return *inner_; + } + + DomainT& GetInnerDomain() { + if (inner_ == nullptr) { + PopulateInner(std::index_sequence_for<Args...>{}); + } + return *inner_; + } + + mutable std::unique_ptr<DomainT> inner_ = nullptr; + // Arguments passed to the inner domain constructor. Set iff inner_ == + // nullptr. + mutable std::optional<std::tuple<Args...>> args_; + // Must be the default value if inner_ != nullptr. + mutable std::function<void(DomainT&)> setup_; +}; + +} // namespace internal +} // namespace fuzztest + +#endif // FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_LAZY_H_
diff --git a/fuzztest/llvm_fuzzer_main.cc b/fuzztest/llvm_fuzzer_main.cc index a170236..f9b17c3 100644 --- a/fuzztest/llvm_fuzzer_main.cc +++ b/fuzztest/llvm_fuzzer_main.cc
@@ -1,3 +1,4 @@ +#include <cstddef> #include <string> #include "gtest/gtest.h" @@ -14,6 +15,9 @@ ABSL_FLAG(std::string, llvm_fuzzer_wrapper_corpus_dir, "", "Path to seed corpus directory used by the wrapped legacy LLVMFuzzer " "target (https://llvm.org/docs/LibFuzzer.html#fuzz-target)."); +ABSL_FLAG(size_t, llvm_fuzzer_wrapper_max_input_size, 4096, + "Maximum input size for the wrapped legacy LLVMFuzzer target " + "(https://llvm.org/docs/LibFuzzer.html#fuzz-target)."); int main(int argc, char** argv) { absl::ParseCommandLine(argc, argv);
diff --git a/fuzztest/llvm_fuzzer_wrapper.cc b/fuzztest/llvm_fuzzer_wrapper.cc index 5340220..6407725 100644 --- a/fuzztest/llvm_fuzzer_wrapper.cc +++ b/fuzztest/llvm_fuzzer_wrapper.cc
@@ -21,12 +21,12 @@ #include "./fuzztest/internal/domains/arbitrary_impl.h" #include "./fuzztest/internal/domains/container_of_impl.h" #include "./fuzztest/internal/domains/domain_base.h" +#include "./fuzztest/internal/domains/lazy.h" #include "./fuzztest/internal/io.h" ABSL_DECLARE_FLAG(std::string, llvm_fuzzer_wrapper_dict_file); ABSL_DECLARE_FLAG(std::string, llvm_fuzzer_wrapper_corpus_dir); - -constexpr static size_t kByteArrayMaxLen = 4096; +ABSL_DECLARE_FLAG(size_t, llvm_fuzzer_wrapper_max_input_size); extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); @@ -56,7 +56,10 @@ for (const fuzztest::internal::FilePathAndData& file : files) { out.push_back( {file.data.begin(), - file.data.begin() + std::min(file.data.size(), kByteArrayMaxLen)}); + file.data.begin() + + std::min( + file.data.size(), + absl::GetFlag(FLAGS_llvm_fuzzer_wrapper_max_input_size))}); } return out; } @@ -177,16 +180,17 @@ public: using typename Base::corpus_type; - ArbitraryByteVector() { WithMaxSize(kByteArrayMaxLen); } + ArbitraryByteVector() = default; void Mutate(corpus_type& val, absl::BitGenRef prng, const MutationMetadata& metadata, bool only_shrink) { if (LLVMFuzzerCustomMutator) { const size_t size = val.size(); - const size_t max_size = only_shrink ? size : kByteArrayMaxLen; - val.resize(max_size); + const size_t mutant_max_size = only_shrink ? size : max_size(); + val.resize(mutant_max_size); mutation_metadata_manager->Activate(metadata); - val.resize(LLVMFuzzerCustomMutator(val.data(), size, max_size, prng())); + val.resize( + LLVMFuzzerCustomMutator(val.data(), size, mutant_max_size, prng())); mutation_metadata_manager->Deactivate(); } else { Base::Mutate(val, prng, metadata, only_shrink); @@ -199,6 +203,9 @@ } FUZZ_TEST(LLVMFuzzer, TestOneInput) - .WithDomains(ArbitraryByteVector() - .WithDictionary(ReadByteArrayDictionaryFromFile) - .WithSeeds(ReadByteArraysFromDirectory)); + .WithDomains(fuzztest::internal::Lazy<ArbitraryByteVector>().WithLazySetup( + [](auto& d) { + d.WithMaxSize(absl::GetFlag(FLAGS_llvm_fuzzer_wrapper_max_input_size)) + .WithDictionary(ReadByteArrayDictionaryFromFile) + .WithSeeds(ReadByteArraysFromDirectory); + }));