From a015ed7093314a3c35a7b52f4d5cb3cf8f5b296c Mon Sep 17 00:00:00 2001 From: Simon Resch Date: Wed, 8 Oct 2025 10:36:14 +0200 Subject: [PATCH 1/3] fix: don't apply Bean factories for proto enums Using the full original factory to construct a mutator for protobuf enum types would trigger a deeply nested mutator construction attempt on the EnumValueDescriptor class using e.g. the `CachedConstructorMutatorFactory`. While this still ended up at the dedicated enum mutator for most cases, for complex protobuf parameters it could lead to the construction of invalid mutators. At the point where an enum type is reached we now use a simplified factory instead that only deals with repeated or nullable enums. --- .../mutation/mutator/collection/BUILD.bazel | 2 +- .../jazzer/mutation/mutator/lang/BUILD.bazel | 2 +- .../jazzer/mutation/mutator/proto/BUILD.bazel | 2 + .../mutator/proto/BuilderMutatorFactory.java | 54 ++++++++++--------- .../jazzer/mutation/ArgumentsMutatorTest.java | 24 +++++++++ .../jazzer/mutation/BUILD.bazel | 2 + .../jazzer/mutation/mutator/proto/BUILD.bazel | 2 +- 7 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/BUILD.bazel b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/BUILD.bazel index 523818b02..3b0e222d6 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/BUILD.bazel +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/BUILD.bazel @@ -2,7 +2,7 @@ java_library( name = "collection", srcs = glob(["*.java"]), visibility = [ - "//src/main/java/com/code_intelligence/jazzer/mutation/mutator:__pkg__", + "//src/main/java/com/code_intelligence/jazzer/mutation/mutator:__subpackages__", "//src/test/java/com/code_intelligence/jazzer/mutation/mutator:__subpackages__", ], deps = [ diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/BUILD.bazel b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/BUILD.bazel index 31d5a7264..dd80c4fb8 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/BUILD.bazel +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/BUILD.bazel @@ -2,7 +2,7 @@ java_library( name = "lang", srcs = glob(["*.java"]), visibility = [ - "//src/main/java/com/code_intelligence/jazzer/mutation/mutator:__pkg__", + "//src/main/java/com/code_intelligence/jazzer/mutation/mutator:__subpackages__", "//src/test/java/com/code_intelligence/jazzer/mutation/mutator:__subpackages__", ], deps = [ diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BUILD.bazel b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BUILD.bazel index 0f4e5ef4a..1459f7b25 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BUILD.bazel +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BUILD.bazel @@ -13,6 +13,8 @@ java_library( "//src/main/java/com/code_intelligence/jazzer/mutation/api", "//src/main/java/com/code_intelligence/jazzer/mutation/combinator", "//src/main/java/com/code_intelligence/jazzer/mutation/engine", + "//src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection", + "//src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang", "//src/main/java/com/code_intelligence/jazzer/mutation/support", "//src/main/java/com/code_intelligence/jazzer/mutation/utils", ], diff --git a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorFactory.java b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorFactory.java index 415166100..e1d4bae75 100644 --- a/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorFactory.java +++ b/src/main/java/com/code_intelligence/jazzer/mutation/mutator/proto/BuilderMutatorFactory.java @@ -53,6 +53,8 @@ import com.code_intelligence.jazzer.mutation.api.SerializingInPlaceMutator; import com.code_intelligence.jazzer.mutation.api.SerializingMutator; import com.code_intelligence.jazzer.mutation.engine.ChainedMutatorFactory; +import com.code_intelligence.jazzer.mutation.mutator.collection.CollectionMutators; +import com.code_intelligence.jazzer.mutation.mutator.lang.LangMutators; import com.code_intelligence.jazzer.mutation.support.Preconditions; import com.google.protobuf.Any; import com.google.protobuf.Descriptors.Descriptor; @@ -140,31 +142,35 @@ private ExtendedMutatorFactory withDescriptorDependentMutatorFactoryIfNeeded( if (field.getJavaType() == JavaType.ENUM) { // Proto enum fields are special as their type (EnumValueDescriptor) does not encode their // domain - we need the actual EnumDescriptor instance. + MutatorFactory enumFactory = + (type, factory) -> + asSubclassOrEmpty(type, EnumValueDescriptor.class) + .map( + unused -> { + EnumDescriptor enumType = field.getEnumType(); + List values = enumType.getValues(); + String name = enumType.getName(); + if (values.size() == 1) { + // While we generally prefer to error out instead of creating a + // mutator that can't actually mutate its domain, we can't do that + // for proto enum fields as the user creating the fuzz test may not be + // in a position to modify the existing proto definition. + return fixedValue(values.get(0)); + } else { + return mutateThenMapToImmutable( + mutateIndices(values.size()), + values::get, + EnumValueDescriptor::getIndex, + unused2 -> "Enum<" + name + ">"); + } + }); + // Apart from the specific enum factory we need handling for `null` and other collection + // types. Note, that we don't include the full original factory here because we don't want + // to follow constructors or builders of the EnumValueDescriptor class. return ChainedMutatorFactory.of( - originalFactory.getCache(), - Stream.of( - originalFactory, - (type, factory) -> - asSubclassOrEmpty(type, EnumValueDescriptor.class) - .map( - unused -> { - EnumDescriptor enumType = field.getEnumType(); - List values = enumType.getValues(); - String name = enumType.getName(); - if (values.size() == 1) { - // While we generally prefer to error out instead of creating a - // mutator that can't actually mutate its domain, we can't do that for - // proto enum fields as the user creating the fuzz test may not be in - // a position to modify the existing proto definition. - return fixedValue(values.get(0)); - } else { - return mutateThenMapToImmutable( - mutateIndices(values.size()), - values::get, - EnumValueDescriptor::getIndex, - unused2 -> "Enum<" + name + ">"); - } - }))); + Stream.concat( + Stream.concat(LangMutators.newFactories(), CollectionMutators.newFactories()), + Stream.of(enumFactory))); } else if (field.getJavaType() == JavaType.MESSAGE) { Descriptor messageDescriptor; if (field.isMapField()) { diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/ArgumentsMutatorTest.java b/src/test/java/com/code_intelligence/jazzer/mutation/ArgumentsMutatorTest.java index d2920de69..fe9cd895e 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/ArgumentsMutatorTest.java +++ b/src/test/java/com/code_intelligence/jazzer/mutation/ArgumentsMutatorTest.java @@ -23,6 +23,8 @@ import com.code_intelligence.jazzer.mutation.annotation.NotNull; import com.code_intelligence.jazzer.mutation.mutator.Mutators; import com.code_intelligence.jazzer.mutation.support.TestSupport.MockPseudoRandom; +import com.code_intelligence.jazzer.protobuf.Proto2.TestProtobuf; +import com.google.protobuf.DescriptorProtos; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.lang.reflect.Method; @@ -347,4 +349,26 @@ void testReadEmptyBeanWithRuntimeError() throws NoSuchMethodException { // expected } } + + // Regression: ensure we can build an ArgumentsMutator for a fuzz target that + // takes a protobuf message and a protobuf descriptor as parameters. + @SuppressWarnings("unused") + public static void complexProtoFuzzTest( + @NotNull TestProtobuf proto, @NotNull DescriptorProtos.DescriptorProto descriptor) {} + + @Test + void testProtoAndDescriptorParameters() throws NoSuchMethodException { + Method method = + ArgumentsMutatorTest.class.getMethod( + "complexProtoFuzzTest", TestProtobuf.class, DescriptorProtos.DescriptorProto.class); + Optional maybeMutator = + ArgumentsMutator.forMethod(Mutators.newFactory(), method); + assertThat(maybeMutator).isPresent(); + // Ensure that the mutator debug string can be generated. This would fail with the error: + // Caused by: java.lang.NullPointerException: Cannot invoke + // + // "com.code_intelligence.jazzer.mutation.api.SerializingMutator.toDebugString(java.util.function.Predicate)" + // because "productMutator" is null + assertThat(maybeMutator.toString()).contains("-> Message"); + } } diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/BUILD.bazel b/src/test/java/com/code_intelligence/jazzer/mutation/BUILD.bazel index 9d3975703..e8027069f 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/BUILD.bazel +++ b/src/test/java/com/code_intelligence/jazzer/mutation/BUILD.bazel @@ -10,6 +10,8 @@ java_test_suite( "//src/main/java/com/code_intelligence/jazzer/mutation/annotation", "//src/main/java/com/code_intelligence/jazzer/mutation/api", "//src/main/java/com/code_intelligence/jazzer/mutation/mutator", + "//src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto:proto2_java_proto", "//src/test/java/com/code_intelligence/jazzer/mutation/support:test_support", + "@protobuf//java/core", ], ) diff --git a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BUILD.bazel b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BUILD.bazel index 17081d854..69d243fba 100644 --- a/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BUILD.bazel +++ b/src/test/java/com/code_intelligence/jazzer/mutation/mutator/proto/BUILD.bazel @@ -28,7 +28,7 @@ java_proto_library( testonly = True, visibility = [ "//selffuzz:__subpackages__", - "//src/test/java/com/code_intelligence/jazzer/mutation/mutator:__pkg__", + "//src/test/java/com/code_intelligence/jazzer/mutation:__subpackages__", "//tests:__pkg__", ], deps = [":proto2_proto"], From fc50d6d8f58455a633eb7eca3f691631e10b033c Mon Sep 17 00:00:00 2001 From: Simon Resch Date: Fri, 26 Sep 2025 16:10:10 +0200 Subject: [PATCH 2/3] chore: update protobuf and abseil --- MODULE.bazel | 23 ++++-------- maven_install.json | 35 +++---------------- .../protobuf-disable-layering_check.patch | 29 --------------- 3 files changed, 10 insertions(+), 77 deletions(-) delete mode 100644 third_party/protobuf-disable-layering_check.patch diff --git a/MODULE.bazel b/MODULE.bazel index 062950ea9..7d25f1d63 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -6,15 +6,15 @@ module(name = "jazzer") # Kept up-to-date by Renovate ################################################################################ -bazel_dep(name = "abseil-cpp", version = "20230802.1") +bazel_dep(name = "abseil-cpp", version = "20250814.1") bazel_dep(name = "apple_support", version = "1.11.1") bazel_dep(name = "bazel_jar_jar", version = "0.1.0") bazel_dep(name = "bazel_skylib", version = "1.7.1") bazel_dep(name = "buildifier_prebuilt", version = "6.4.0") bazel_dep(name = "contrib_rules_jvm", version = "0.24.0") -bazel_dep(name = "googletest", version = "1.14.0.bcr.1") +bazel_dep(name = "googletest", version = "1.17.0.bcr.1") bazel_dep(name = "platforms", version = "0.0.10") -bazel_dep(name = "protobuf") +bazel_dep(name = "protobuf", version = "32.1") bazel_dep(name = "rules_android", version = "0.1.1") bazel_dep(name = "rules_android_ndk", version = "0.1.2") bazel_dep(name = "rules_foreign_cc", version = "0.11.1") @@ -35,17 +35,6 @@ bazel_dep(name = "rules_license", version = "1.0.0") bazel_dep(name = "rules_pkg", version = "0.9.1") bazel_dep(name = "toolchains_llvm", version = "0.10.3") -# TODO: Remove after a release that includes https://github.com/protocolbuffers/protobuf/commit/a7b0421c78412baa48704d727601a17ac0f451d1. -single_version_override( - module_name = "protobuf", - patch_strip = 1, - patches = [ - # https://github.com/protocolbuffers/protobuf/pull/15332 - "//third_party:protobuf-disable-layering_check.patch", - ], - version = "23.1", -) - ################################################################################ # Maven dependencies ################################################################################ @@ -371,9 +360,9 @@ cc_library( visibility = ["//visibility:public"], ) """, - sha256 = "21bfdfef25554fa2e30aec2a9f9b58f4a17c1d8c8593763fa94a6dd74b226594", - strip_prefix = "libprotobuf-mutator-3b28530531b154a748fe9884bc9219b4966f0754", - urls = ["https://github.com/google/libprotobuf-mutator/archive/3b28530531b154a748fe9884bc9219b4966f0754.tar.gz"], + integrity = "sha256-iVlYiBtJk99wtPZSwtgsW9l9IvgByk9DDWVGgJ3yk9U=", + strip_prefix = "libprotobuf-mutator-1.5", + urls = ["https://github.com/google/libprotobuf-mutator/archive/refs/tags/v1.5.tar.gz"], ) ################################################################################ diff --git a/maven_install.json b/maven_install.json index 58d0858a5..f64d1aa3e 100755 --- a/maven_install.json +++ b/maven_install.json @@ -1,14 +1,11 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": 285652305, - "__RESOLVED_ARTIFACTS_HASH": -2054117877, + "__INPUT_ARTIFACTS_HASH": -1379382746, + "__RESOLVED_ARTIFACTS_HASH": -930880548, "conflict_resolution": { "com.google.code.gson:gson:2.8.6": "com.google.code.gson:gson:2.8.9", - "com.google.errorprone:error_prone_annotations:2.3.2": "com.google.errorprone:error_prone_annotations:2.26.1", - "com.google.guava:guava:31.1-jre": "com.google.guava:guava:33.0.0-android", - "com.google.j2objc:j2objc-annotations:1.3": "com.google.j2objc:j2objc-annotations:2.8", - "com.google.truth:truth:1.1.2": "com.google.truth:truth:1.4.2", - "org.mockito:mockito-core:4.3.1": "org.mockito:mockito-core:5.11.0" + "com.google.errorprone:error_prone_annotations:2.5.1": "com.google.errorprone:error_prone_annotations:2.26.1", + "com.google.guava:guava:32.0.1-jre": "com.google.guava:guava:33.0.0-android" }, "artifacts": { "com.alibaba:fastjson": { @@ -101,12 +98,6 @@ }, "version": "33.0.0-android" }, - "com.google.guava:guava-testlib": { - "shasums": { - "jar": "aadc71b10d5c3ac474dd16be84cfb18d257e584d1e0a59f8cab64ef4376226ce" - }, - "version": "31.1-jre" - }, "com.google.guava:listenablefuture": { "shasums": { "jar": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99" @@ -669,14 +660,6 @@ "com.google.j2objc:j2objc-annotations", "org.checkerframework:checker-qual" ], - "com.google.guava:guava-testlib": [ - "com.google.code.findbugs:jsr305", - "com.google.errorprone:error_prone_annotations", - "com.google.guava:guava", - "com.google.j2objc:j2objc-annotations", - "junit:junit", - "org.checkerframework:checker-qual" - ], "com.google.truth.extensions:truth-java8-extension": [ "com.google.truth:truth", "org.checkerframework:checker-qual" @@ -1123,15 +1106,6 @@ "com.google.common.xml", "com.google.thirdparty.publicsuffix" ], - "com.google.guava:guava-testlib": [ - "com.google.common.collect.testing", - "com.google.common.collect.testing.features", - "com.google.common.collect.testing.google", - "com.google.common.collect.testing.testers", - "com.google.common.escape.testing", - "com.google.common.testing", - "com.google.common.util.concurrent.testing" - ], "com.google.j2objc:j2objc-annotations": [ "com.google.j2objc.annotations" ], @@ -2595,7 +2569,6 @@ "com.google.errorprone:error_prone_type_annotations", "com.google.guava:failureaccess", "com.google.guava:guava", - "com.google.guava:guava-testlib", "com.google.guava:listenablefuture", "com.google.j2objc:j2objc-annotations", "com.google.protobuf:protobuf-java", diff --git a/third_party/protobuf-disable-layering_check.patch b/third_party/protobuf-disable-layering_check.patch deleted file mode 100644 index c16b44a34..000000000 --- a/third_party/protobuf-disable-layering_check.patch +++ /dev/null @@ -1,29 +0,0 @@ -From f4444a81218ede5eb58306bd57eaefb5d9ffd9e2 Mon Sep 17 00:00:00 2001 -From: Fabian Meumertzheim -Date: Mon, 8 Jan 2024 13:30:32 +0100 -Subject: [PATCH] Disable unsupported `layering_check` Bazel feature - -This allows downstream projects to use `layering_check` without having -to patch Protobuf to disable the feature for the repository. ---- - REPO.bazel | 11 +++++++++++ - 1 file changed, 11 insertions(+) - create mode 100644 REPO.bazel - -diff --git a/REPO.bazel b/REPO.bazel -new file mode 100644 -index 00000000000..a537ac745fa ---- /dev/null -+++ b/REPO.bazel -@@ -0,0 +1,11 @@ -+# This file is read by Bazel 7 and newer, both if Protobuf is the main -+# repository and if it is an external repository. -+repo( -+ features = [ -+ # Protobuf cc_* targets do not specify all dependencies from which they -+ # include headers. This causes builds of downstream projects with -+ # --feature=layering_check to fail, which can be avoided by disabling -+ # the feature for the entire repository. -+ "-layering_check", -+ ], -+) From 0e762a8ed79f8395581005a141ef0b2d36ebbe80 Mon Sep 17 00:00:00 2001 From: Simon Resch Date: Tue, 30 Sep 2025 08:59:10 +0200 Subject: [PATCH 3/3] chore: set -std=c++17 for macos & windows builds Recent versions of googletest require C++17 to compile. --- .bazelrc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.bazelrc b/.bazelrc index d07e43ac9..4683cc01e 100644 --- a/.bazelrc +++ b/.bazelrc @@ -26,6 +26,8 @@ build --features=layering_check build:macos --apple_crosstool_top=@local_config_apple_cc//:toolchain build:macos --crosstool_top=@local_config_apple_cc//:toolchain build:macos --host_crosstool_top=@local_config_apple_cc//:toolchain +build:macos --cxxopt=-std=c++17 +build:macos --host_cxxopt=-std=c++17 # Java # Always build for Java 8, even with a newer JDK. This ensures that all artifacts we release are @@ -64,6 +66,8 @@ build --android_platforms=//:android_arm64 build:windows --extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows-clang-cl build:windows --extra_execution_platforms=//bazel/platforms:x64_windows-clang-cl build:windows --features=static_link_msvcrt +build:windows --cxxopt=/std:c++17 +build:windows --host_cxxopt=/std:c++17 # Required as PATH doubles as the shared library search path on Windows and the # Java agent functionality depends on system-provided shared libraries. test:windows --noincompatible_strict_action_env