Skip to content

Commit

Permalink
Overlap the UTF8 string domains with String() to use the dictionary m…
Browse files Browse the repository at this point in the history
…utations.

Attempt 2 without changing the corpus format.

PiperOrigin-RevId: 692700332
  • Loading branch information
xinhaoyuan authored and copybara-github committed Nov 4, 2024
1 parent c12eb40 commit 25b32f1
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 7 deletions.
2 changes: 2 additions & 0 deletions domain_tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ cc_test(
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/numeric:int128",
"@com_google_absl//absl/random",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
"@com_google_fuzztest//fuzztest:domain_core",
"@com_google_fuzztest//fuzztest:meta",
Expand Down Expand Up @@ -240,6 +241,7 @@ cc_test(
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/random",
"@com_google_fuzztest//fuzztest:domain_core",
"@com_google_fuzztest//fuzztest:table_of_recent_compares",
"@com_google_googletest//:gtest_main",
],
)
1 change: 1 addition & 0 deletions domain_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -239,5 +239,6 @@ fuzztest_cc_test(
absl::flat_hash_set
absl::random_random
fuzztest::domain_core
fuzztest::table_of_recent_compares
GTest::gmock_main
)
75 changes: 75 additions & 0 deletions domain_tests/misc_domains_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
// in this directory: BitFlagCombinationOf, OneOf, and OverlapOf.

#include <cstdlib>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <variant>
#include <vector>
Expand All @@ -26,13 +28,27 @@
#include "absl/container/flat_hash_set.h"
#include "absl/numeric/int128.h"
#include "absl/random/random.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/types/span.h"
#include "./fuzztest/domain_core.h"
#include "./domain_tests/domain_testing.h"
#include "./fuzztest/internal/meta.h"
#include "./fuzztest/internal/type_support.h"

namespace fuzztest {
namespace internal {

class OverlapOfTestPeer {
public:
template <typename D>
static void SetSerializationDomain(D& domain, size_t index) {
domain.WithSerializationDomain(index);
}
};

} // namespace internal

namespace {

using ::testing::AllOf;
Expand Down Expand Up @@ -232,6 +248,65 @@ TEST(OverlapOf, GeneratesMultipleValidValues) {
AllOf(Each(AllOf(Ge(1), Le(3))), IsSupersetOf({1, 2, 3})));
}

TEST(OverlapOf, UsesSerializationDomain) {
auto domain_0 = Arbitrary<std::string>();
auto domain_1 =
ReversibleMap([](int x) -> std::string { return absl::StrCat(x); },
[](const std::string& s) -> std::optional<std::tuple<int>> {
int result = 0;
if (!absl::SimpleAtoi(s, &result)) return std::nullopt;
return result;
},
Arbitrary<int>());
auto overlapped_domain = OverlapOf(domain_0, domain_1);
for (const auto& v : GenerateNonUniqueValues(overlapped_domain)) {
auto domain_0_corpus = domain_0.FromValue(v.user_value);
ASSERT_TRUE(domain_0_corpus.has_value());
auto domain_1_corpus = domain_1.FromValue(v.user_value);
ASSERT_TRUE(domain_1_corpus.has_value());
EXPECT_NE(overlapped_domain.SerializeCorpus(v.corpus_value).ToString(),
domain_0.SerializeCorpus(*domain_0_corpus).ToString())
<< "Expect different serialized corpora before "
"`WithSerializationDomain(...)`";
EXPECT_NE(overlapped_domain.SerializeCorpus(v.corpus_value).ToString(),
domain_1.SerializeCorpus(*domain_1_corpus).ToString())
<< "Expect different serialized corpora before "
"`WithSerializationDomain(...)`";
}

internal::OverlapOfTestPeer::SetSerializationDomain(overlapped_domain, 0);
for (const auto& v : GenerateNonUniqueValues(overlapped_domain)) {
auto domain_0_corpus = domain_0.FromValue(v.user_value);
ASSERT_TRUE(domain_0_corpus.has_value());
auto domain_1_corpus = domain_1.FromValue(v.user_value);
ASSERT_TRUE(domain_1_corpus.has_value());
EXPECT_EQ(overlapped_domain.SerializeCorpus(v.corpus_value).ToString(),
domain_0.SerializeCorpus(*domain_0_corpus).ToString())
<< "Expect the same serialized corpora after "
"`WithSerializationDomain(0)`";
EXPECT_NE(overlapped_domain.SerializeCorpus(v.corpus_value).ToString(),
domain_1.SerializeCorpus(*domain_1_corpus).ToString())
<< "Expect different serialized corpora after "
"`WithSerializationDomain(0)`";
}

internal::OverlapOfTestPeer::SetSerializationDomain(overlapped_domain, 1);
for (const auto& v : GenerateNonUniqueValues(overlapped_domain)) {
auto domain_0_corpus = domain_0.FromValue(v.user_value);
ASSERT_TRUE(domain_0_corpus.has_value());
auto domain_1_corpus = domain_1.FromValue(v.user_value);
ASSERT_TRUE(domain_1_corpus.has_value());
EXPECT_NE(overlapped_domain.SerializeCorpus(v.corpus_value).ToString(),
domain_0.SerializeCorpus(*domain_0_corpus).ToString())
<< "Expect different serialized corpora after "
"`WithSerializationDomain(1)`";
EXPECT_EQ(overlapped_domain.SerializeCorpus(v.corpus_value).ToString(),
domain_1.SerializeCorpus(*domain_1_corpus).ToString())
<< "Expect the same serialized corpora after "
"`WithSerializationDomain(1)`";
}
}

} // namespace
} // namespace fuzztest

Expand Down
2 changes: 2 additions & 0 deletions domain_tests/string_domains_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// Tests of character and string domains.

#include <cctype>
#include <cstdint>
#include <deque>
#include <optional>
#include <string>
Expand All @@ -27,6 +28,7 @@
#include "absl/random/random.h"
#include "./fuzztest/domain_core.h"
#include "./domain_tests/domain_testing.h"
#include "./fuzztest/internal/table_of_recent_compares.h"

namespace fuzztest {
namespace {
Expand Down
6 changes: 6 additions & 0 deletions e2e_tests/testdata/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,25 @@ cc_binary(
"fuzz_tests_for_functional_testing.cc",
"fuzz_tests_for_microbenchmarking.cc",
"fuzz_tests_using_googletest.cc",
"fuzz_tests_with_proto_inputs.cc",
],
deps = [
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/functional:function_ref",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:span",
"@com_google_fuzztest//fuzztest",
"@com_google_fuzztest//fuzztest:fuzztest_gtest_main",
"@com_google_fuzztest//fuzztest:googletest_fixture_adapter",
"@com_google_fuzztest//fuzztest:logging",
"@com_google_fuzztest//fuzztest:test_protobuf_cc_proto",
"@com_google_protobuf//:protobuf",
"@com_googlesource_code_re2//:re2",
],
)

Expand Down
6 changes: 6 additions & 0 deletions e2e_tests/testdata/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,23 @@ add_executable(
fuzz_tests_for_functional_testing.cc
fuzz_tests_for_microbenchmarking.cc
fuzz_tests_using_googletest.cc
fuzz_tests_with_proto_inputs.cc
)
target_link_libraries(
fuzz_tests_for_functional_testing.stripped
PUBLIC
protobuf::libprotobuf
test_protobuf
absl::check
absl::algorithm_container
absl::flat_hash_map
absl::flat_hash_set
absl::function_ref
absl::span
absl::str_format
absl::strings
absl::time
re2::re2
fuzztest_googletest_fixture_adapter
fuzztest_logging
)
Expand Down
2 changes: 1 addition & 1 deletion fuzztest/internal/centipede_adaptor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ class CentipedeAdaptorRunnerCallbacks : public centipede::RunnerCallbacks {
Runtime& runtime_;
FuzzTestFuzzerImpl& fuzzer_impl_;
const Configuration& configuration_;
internal::TablesOfRecentCompares cmp_tables_;
TablesOfRecentCompares cmp_tables_;
absl::BitGen prng_;
};

Expand Down
58 changes: 52 additions & 6 deletions fuzztest/internal/domains/overlap_of_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
#include "./fuzztest/internal/status.h"
#include "./fuzztest/internal/type_support.h"

namespace fuzztest::internal_no_adl {
class Utf8StringHelper;
} // namespace fuzztest::internal_no_adl

namespace fuzztest::internal {

template <typename... Inner>
Expand Down Expand Up @@ -107,7 +111,7 @@ class OverlapOfImpl
ValidateCorpusValueForOtherDomains(*mutant_corpus).ok();
MaybeReportOnValidationResult(valid);
if (valid) {
val = *mutant_corpus;
val = *std::move(mutant_corpus);
return;
}
}
Expand All @@ -122,9 +126,10 @@ class OverlapOfImpl

std::optional<corpus_type> FromValue(const value_type& v) const {
std::optional<corpus_type> corpus;
// Always use the first inner domain to get the corpus value. We could use
// other domains but there is little difference.
Switch<kNumDomains>(0, [&](auto I) {
// Unless the serialization domain is set, use the first inner domain to get
// the corpus value. We could use other domains but there is little
// difference.
Switch<kNumDomains>(serialization_domain_index_.value_or(0), [&](auto I) {
auto inner_corpus = std::get<I>(domains_).FromValue(v);
if (!inner_corpus.has_value()) return;
corpus = corpus_type(std::in_place_index<I>, *std::move(inner_corpus));
Expand All @@ -138,11 +143,39 @@ class OverlapOfImpl
auto GetPrinter() const { return VariantPrinter<Inner...>{domains_}; }

std::optional<corpus_type> ParseCorpus(const IRObject& obj) const {
return ParseWithDomainVariant(domains_, obj);
if (!serialization_domain_index_.has_value()) {
return ParseWithDomainVariant(domains_, obj);
}
return Switch<kNumDomains>(
*serialization_domain_index_,
[&](auto I) -> std::optional<corpus_type> {
auto inner_corpus = std::get<I>(domains_).ParseCorpus(obj);
if (!inner_corpus.has_value()) return std::nullopt;
return corpus_type(std::in_place_index<I>, *std::move(inner_corpus));
});
}

IRObject SerializeCorpus(const corpus_type& v) const {
return SerializeWithDomainVariant(domains_, v);
if (!serialization_domain_index_.has_value()) {
return SerializeWithDomainVariant(domains_, v);
}
if (*serialization_domain_index_ == v.index()) {
return Switch<kNumDomains>(*serialization_domain_index_, [&](auto I) {
return std::get<I>(domains_).SerializeCorpus(std::get<I>(v));
});
}
const auto user_value = Switch<kNumDomains>(v.index(), [&](auto I) {
auto& domain = std::get<I>(domains_);
return domain.GetValue(std::get<I>(v));
});
return Switch<kNumDomains>(*serialization_domain_index_, [&](auto I) {
auto& domain = std::get<I>(domains_);
const auto inner_corpus = domain.FromValue(user_value);
FUZZTEST_INTERNAL_CHECK(inner_corpus.has_value(),
"Mutate() called on a user value that is not "
"valid in all overlapping domains");
return domain.SerializeCorpus(*inner_corpus);
});
}

absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const {
Expand All @@ -155,14 +188,27 @@ class OverlapOfImpl
}

private:
friend class OverlapOfTestPeer;
friend class internal_no_adl::Utf8StringHelper;

static constexpr size_t kNumDomains = sizeof...(Inner);
static_assert(kNumDomains > 1,
"It requires more than one domain to overlap.");

std::tuple<Inner...> domains_;
std::optional<size_t> serialization_domain_index_;
size_t num_validation_attempts_ = 0;
size_t num_validation_failures_ = 0;

OverlapOfImpl& WithSerializationDomain(size_t index) {
FUZZTEST_INTERNAL_CHECK_PRECONDITION(
index < kNumDomains,
absl::StrFormat("Serialization domain index must be less than %d",
kNumDomains));
serialization_domain_index_ = index;
return *this;
}

absl::Status ValidateCorpusValueForOtherDomains(
const corpus_type& corpus_value) const {
const auto value = Switch<kNumDomains>(corpus_value.index(), [&](auto I) {
Expand Down

0 comments on commit 25b32f1

Please sign in to comment.