From b4c8d2adccd769fbbafb685e040697919feb8f70 Mon Sep 17 00:00:00 2001 From: psy Date: Tue, 13 Jan 2026 16:23:33 +0100 Subject: [PATCH] feat: add hook for Enum.valueOf --- .../jazzer/runtime/TraceCmpHooks.java | 47 +++++++++++++++++++ tests/BUILD.bazel | 16 ++++++- .../src/test/java/com/example/EnumFuzzer.java | 39 +++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 tests/src/test/java/com/example/EnumFuzzer.java diff --git a/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java b/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java index e2b8e5625..53fa7e068 100644 --- a/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java +++ b/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java @@ -866,6 +866,53 @@ public static void mapGetOrDefault( } } + @MethodHook( + type = HookType.BEFORE, + targetClassName = "java.lang.Enum", + targetMethod = "valueOf", + targetMethodDescriptor = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;") + public static void enumValueOf( + MethodHandle method, Object alwaysNull, Object[] arguments, int hookId) { + if (arguments.length < 2) { + return; + } + Object enumTypeArg = arguments[0]; + Object nameArg = arguments[1]; + if (!(enumTypeArg instanceof Class) || !(nameArg instanceof String)) { + return; + } + Class enumClass = (Class) enumTypeArg; + if (enumClass == null || !enumClass.isEnum()) { + return; + } + String candidate = (String) nameArg; + if (candidate == null) { + return; + } + + Enum[] constants; + try { + constants = (Enum[]) enumClass.getEnumConstants(); + } catch (Exception | LinkageError ignored) { + return; + } + if (constants == null || constants.length == 0) { + return; + } + + // Skip guidance if the target string is already valid. + for (Enum enumConstant : constants) { + if (enumConstant.name().equals(candidate)) { + return; + } + } + + // Guide the fuzzer towards a single valid enum + int index = Math.floorMod(candidate.hashCode(), constants.length); + Enum target = constants[index]; + TraceDataFlowNativeCallbacks.traceStrcmp(candidate, target.name(), 1, hookId); + } + private static final class Bounds { private final Object lower; private final Object upper; diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel index b16f01c87..2fe4fe4b0 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel @@ -701,7 +701,7 @@ java_fuzz_target_test( srcs = ["src/test/java/com/example/SetFuzzer.java"], allowed_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium"], fuzzer_args = [ - "-runs=1000000", + "-runs=100000", ], target_class = "com.example.SetFuzzer", verify_crash_reproducer = False, @@ -710,6 +710,20 @@ java_fuzz_target_test( ], ) +java_fuzz_target_test( + name = "EnumFuzzer", + srcs = ["src/test/java/com/example/EnumFuzzer.java"], + allowed_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium"], + fuzzer_args = [ + "-runs=100000", + ], + target_class = "com.example.EnumFuzzer", + verify_crash_reproducer = False, + deps = [ + "//src/main/java/com/code_intelligence/jazzer/mutation/annotation", + ], +) + sh_test( name = "jazzer_from_path_test", srcs = ["src/test/shell/jazzer_from_path_test.sh"], diff --git a/tests/src/test/java/com/example/EnumFuzzer.java b/tests/src/test/java/com/example/EnumFuzzer.java new file mode 100644 index 000000000..9f1883efb --- /dev/null +++ b/tests/src/test/java/com/example/EnumFuzzer.java @@ -0,0 +1,39 @@ +/* + * Copyright 2024 Code Intelligence GmbH + * + * 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. + */ + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium; +import com.code_intelligence.jazzer.mutation.annotation.NotNull; + +public class EnumFuzzer { + private enum SampleColor { + RED, + GREEN, + BLUE, + SUPER_SECRET_SHADE + } + + public static void fuzzerTestOneInput(@NotNull String value) { + try { + SampleColor color = SampleColor.valueOf(value); + if (color == SampleColor.SUPER_SECRET_SHADE) { + throw new FuzzerSecurityIssueMedium("Enum matched: " + color); + } + } catch (IllegalArgumentException ignored) { + } + } +}