From 37cad365b9097018d9d6c5a7923a2aeaa43a3a4b Mon Sep 17 00:00:00 2001 From: Hadi Ravanbakhsh Date: Mon, 3 Mar 2025 11:10:00 -0800 Subject: [PATCH] Add ValidateCorpusValue to the InGrammar domains. PiperOrigin-RevId: 732989178 --- fuzztest/internal/domains/in_grammar_impl.h | 150 +++++++++++++++----- 1 file changed, 113 insertions(+), 37 deletions(-) diff --git a/fuzztest/internal/domains/in_grammar_impl.h b/fuzztest/internal/domains/in_grammar_impl.h index 449ec0eb..1d4d7a77 100644 --- a/fuzztest/internal/domains/in_grammar_impl.h +++ b/fuzztest/internal/domains/in_grammar_impl.h @@ -23,6 +23,7 @@ #include #include +#include "absl/base/attributes.h" #include "absl/container/flat_hash_map.h" #include "absl/random/bit_gen_ref.h" #include "absl/random/distributions.h" @@ -134,6 +135,13 @@ class StringLiteralDomain { result.children.emplace(); return result; } + + static absl::Status ValidateCorpusValue(const ASTNode& astnode) { + if (!CheckASTNodeTypeIdAndChildType(astnode, id)) { + return absl::InvalidArgumentError("Invalid node type!"); + } + return absl::OkStatus(); + } }; template @@ -151,23 +159,25 @@ class RegexLiteralDomain { static void Mutate(ASTNode& val, absl::BitGenRef prng, const domain_implementor::MutationMetadata& metadata, bool only_shrink) { - GetInnerRegexpDomain().Mutate(std::get<1>(val.children), prng, metadata, - only_shrink); + GetInnerRegexpDomain().Mutate(std::get(val.children), prng, + metadata, only_shrink); } static ASTTypeId TypeId() { return id; } static void ToString(std::string& output, const ASTNode& val) { - FUZZTEST_INTERNAL_CHECK(val.children.index() == 1, "Not a regex literal!"); - absl::StrAppend(&output, - GetInnerRegexpDomain().GetValue(std::get<1>(val.children))); + FUZZTEST_INTERNAL_CHECK(CheckASTNodeTypeIdAndChildType(val, id), + "Not a regex literal!"); + absl::StrAppend(&output, GetInnerRegexpDomain().GetValue( + std::get(val.children))); } static bool IsMutable(const ASTNode& /*val*/) { return true; } static IRObject SerializeCorpus(const ASTNode& astnode) { FUZZTEST_INTERNAL_CHECK( - CheckASTNodeTypeIdAndChildType(astnode, id), "Invalid node!"); + CheckASTNodeTypeIdAndChildType(astnode, id), + "Not a regex literal!"); return WrapASTIntoIRObject(astnode, GetInnerRegexpDomain().SerializeCorpus( std::get(astnode.children))); @@ -190,6 +200,13 @@ class RegexLiteralDomain { return result; } + static absl::Status ValidateCorpusValue(const ASTNode& astnode) { + if (!CheckASTNodeTypeIdAndChildType(astnode, id)) { + return absl::InvalidArgumentError("Not a regex literal!"); + } + return absl::OkStatus(); + } + private: static internal::InRegexpImpl& GetInnerRegexpDomain() { static internal::InRegexpImpl* inner_domain = @@ -240,8 +257,11 @@ class VectorDomain { static void Mutate(ASTNode& val, absl::BitGenRef prng, const domain_implementor::MutationMetadata& metadata, bool only_shrink) { - FUZZTEST_INTERNAL_CHECK(val.children.index() == 2, "Not a vector!"); - std::vector& elements = std::get<2>(val.children); + FUZZTEST_INTERNAL_CHECK( + CheckASTNodeTypeIdAndChildType>(val, id), + "Not a vector!"); + std::vector& elements = + std::get>(val.children); if (only_shrink) { ShrinkElements(elements, prng); return; @@ -272,7 +292,7 @@ class VectorDomain { } static void ToString(std::string& output, const ASTNode& val) { - for (const auto& child : std::get<2>(val.children)) { + for (const auto& child : std::get>(val.children)) { ElementT::ToString(output, child); } } @@ -282,7 +302,7 @@ class VectorDomain { static IRObject SerializeCorpus(const ASTNode& astnode) { FUZZTEST_INTERNAL_CHECK( CheckASTNodeTypeIdAndChildType>(astnode, id), - "Invalid node!"); + "Not a vector!"); IRObject expansion_obj; auto& inner_subs = expansion_obj.MutableSubs(); for (auto& node : std::get>(astnode.children)) { @@ -319,6 +339,18 @@ class VectorDomain { return result; } + static absl::Status ValidateCorpusValue(const ASTNode& astnode) { + if (!CheckASTNodeTypeIdAndChildType>(astnode, id)) { + return absl::InvalidArgumentError("Not a vector!"); + } + absl::Status status = absl::OkStatus(); + for (const auto& child : std::get>(astnode.children)) { + status.Update(ElementT::ValidateCorpusValue(child)); + if (!status.ok()) return status; + } + return status; + } + private: static void ShrinkElements(std::vector& elements, absl::BitGenRef prng) { @@ -393,13 +425,14 @@ class TupleDomain { const domain_implementor::MutationMetadata& metadata, bool only_shrink) { FUZZTEST_INTERNAL_CHECK( - val.children.index() == 2 && - std::get<2>(val.children).size() == sizeof...(ElementT), + CheckASTNodeTypeIdAndChildType>(val, id) && + std::get>(val.children).size() == + sizeof...(ElementT), "Tuple elements number doesn't match!"); std::vector mutables; ApplyIndex([&](auto... I) { - ((ElementT::IsMutable(std::get<2>(val.children)[I]) + ((ElementT::IsMutable(std::get>(val.children)[I]) ? mutables.push_back(I) : (void)0), ...); @@ -411,23 +444,28 @@ class TupleDomain { int choice = mutables[absl::Uniform(prng, 0, mutables.size())]; ApplyIndex([&](auto... I) { - ((choice == I ? (ElementT::Mutate(std::get<2>(val.children)[I], prng, - metadata, only_shrink)) - : (void)0), + ((choice == I + ? (ElementT::Mutate(std::get>(val.children)[I], + prng, metadata, only_shrink)) + : (void)0), ...); }); } static void ToString(std::string& output, const ASTNode& val) { ApplyIndex([&](auto... I) { - (ElementT::ToString(output, std::get<2>(val.children)[I]), ...); + (ElementT::ToString(output, + std::get>(val.children)[I]), + ...); }); } static bool IsMutable(const ASTNode& val) { bool result = false; ApplyIndex([&](auto... I) { - result = (ElementT::IsMutable(std::get<2>(val.children)[I]) || ...); + result = (ElementT::IsMutable( + std::get>(val.children)[I]) || + ...); }); return result; } @@ -439,8 +477,8 @@ class TupleDomain { IRObject expansion_obj; auto& inner_subs = expansion_obj.MutableSubs(); ApplyIndex([&](auto... I) { - (inner_subs.push_back( - ElementT::SerializeCorpus(std::get<2>(astnode.children)[I])), + (inner_subs.push_back(ElementT::SerializeCorpus( + std::get>(astnode.children)[I])), ...); }); return WrapASTIntoIRObject(astnode, expansion_obj); @@ -476,6 +514,25 @@ class TupleDomain { } return result; } + + static absl::Status ValidateCorpusValue(const ASTNode& astnode) { + if (!CheckASTNodeTypeIdAndChildType>(astnode, id)) { + return absl::InvalidArgumentError("Not a vector!"); + } + if (std::get>(astnode.children).size() == + sizeof...(ElementT)) { + return absl::InvalidArgumentError("Tuple elements number doesn't match!"); + } + absl::Status status = absl::OkStatus(); + ApplyIndex([&](auto... I) { + auto ABSL_ATTRIBUTE_UNUSED unused = + ((status.Update(ElementT::ValidateCorpusValue( + std::get>(astnode.children)[I])), + status.ok()) && + ...); + }); + return status; + } }; template @@ -493,7 +550,7 @@ class VariantDomain { : absl::Uniform(prng, 0, (sizeof...(ElementT))); ASTNode result{id, std::vector()}; Switch(choice, [&](auto I) { - std::get<2>(result.children) + std::get>(result.children) .push_back(std::tuple_element>::type:: InitWithBudget(prng, generation_budget)); }); @@ -504,7 +561,8 @@ class VariantDomain { const domain_implementor::MutationMetadata& metadata, bool only_shrink) { constexpr bool has_alternative = sizeof...(ElementT) > 1; - ASTNode current_value = std::get<2>(val.children).front(); + ASTNode current_value = + std::get>(val.children).front(); ASTTypeId current_value_id = current_value.type_id; bool is_current_value_mutable; ((ElementT::TypeId() == current_value_id @@ -536,12 +594,12 @@ class VariantDomain { } static void ToString(std::string& output, const ASTNode& val) { - FUZZTEST_INTERNAL_CHECK(std::get<2>(val.children).size() == 1, - "This is not a variant ast node."); - ASTTypeId child_id = std::get<2>(val.children).front().type_id; - ((ElementT::TypeId() == child_id - ? (ElementT::ToString(output, std::get<2>(val.children).front())) - : (void)0), + FUZZTEST_INTERNAL_CHECK( + std::get>(val.children).size() == 1, + "This is not a variant ast node."); + auto child = std::get>(val.children).front(); + ((ElementT::TypeId() == child.type_id ? (ElementT::ToString(output, child)) + : (void)0), ...); } @@ -551,10 +609,9 @@ class VariantDomain { // Otherwise, we check whether the only choice is mutable. bool result = false; - ASTTypeId child_id = std::get<2>(val.children).front().type_id; - ((ElementT::TypeId() == child_id - ? (result = ElementT::IsMutable(std::get<2>(val.children).front()), - (void)0) + auto child = std::get>(val.children).front(); + ((ElementT::TypeId() == child.type_id + ? (result = ElementT::IsMutable(child), (void)0) : (void)0), ...); return result; @@ -603,11 +660,30 @@ class VariantDomain { return result; } + static absl::Status ValidateCorpusValue(const ASTNode& astnode) { + if (!CheckASTNodeTypeIdAndChildType>(astnode, id)) { + return absl::InvalidArgumentError("Invalid node type!"); + } + if (std::get>(astnode.children).size() != 1) { + return absl::InvalidArgumentError("This is not a variant ast node."); + } + absl::Status status = absl::OkStatus(); + auto child = std::get>(astnode.children).front(); + auto ABSL_ATTRIBUTE_UNUSED unused = + ((ElementT::TypeId() == child.type_id + ? (status.Update(ElementT::ValidateCorpusValue(child)), + status.ok()) + : true) && + ...); + return status; + } + private: static void MutateCurrentValue( ASTNode& val, absl::BitGenRef prng, const domain_implementor::MutationMetadata& metadata, bool only_shrink) { - ASTNode& current_value = std::get<2>(val.children).front(); + ASTNode& current_value = + std::get>(val.children).front(); ((ElementT::TypeId() == current_value.type_id ? (ElementT::Mutate(current_value, prng, metadata, only_shrink)) : (void)0), @@ -616,7 +692,8 @@ class VariantDomain { static void SwitchToAlternative(ASTNode& val, absl::BitGenRef prng) { constexpr int n_alternative = sizeof...(ElementT); FUZZTEST_INTERNAL_CHECK(n_alternative > 1, "No alternative to switch!"); - int child_type_id = std::get<2>(val.children).front().type_id; + int child_type_id = + std::get>(val.children).front().type_id; int current_choice = 0; ApplyIndex([&](auto... I) { ((ElementT::TypeId() == child_type_id ? (current_choice = I, (void)0) @@ -630,7 +707,7 @@ class VariantDomain { // Switch to an alternative value. ApplyIndex([&](auto... I) { - ((choice == I ? (std::get<2>(val.children).front() = + ((choice == I ? (std::get>(val.children).front() = ElementT::InitWithBudget(prng, kMaxGenerationNum), (void)0) : (void)0), @@ -690,8 +767,7 @@ class InGrammarImpl absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const { // Validation is currently done during Parsing, and UserToCorpusValue() is // not supported yet. - // TODO(lszekeres): Refactor so that validation happens here instead. - return absl::OkStatus(); + return TopDomain::ValidateCorpusValue(corpus_value); } private: