From ca81c4d28d3e85645144e9f4a141b49c5e55ace8 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 | 41 +++++++++++++++++-- tests/BUILD.bazel | 16 +++++++- .../src/test/java/com/example/EnumFuzzer.java | 39 ++++++++++++++++++ 3 files changed, 91 insertions(+), 5 deletions(-) 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..dd72027ad 100644 --- a/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java +++ b/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Code Intelligence GmbH + * Copyright 2026 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. @@ -625,9 +625,6 @@ public static void indexOfKt( targetMethod = "replaceFirst$default") public static void replaceKt( MethodHandle method, Object alwaysNull, Object[] arguments, int hookId, String returnValue) { - if (arguments.length < 2 || !(arguments[0] instanceof String)) { - return; - } String original = (String) arguments[0]; if (!original.equals(returnValue)) { return; @@ -866,6 +863,42 @@ 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) { + Class enumClass = (Class) arguments[0]; + String candidate = (String) arguments[1]; + if (enumClass == null || 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) { + } + } +}