Skip to content

Commit b4c8d2a

Browse files
committed
feat: add hook for Enum.valueOf
1 parent 305244c commit b4c8d2a

File tree

3 files changed

+101
-1
lines changed

3 files changed

+101
-1
lines changed

src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,53 @@ public static void mapGetOrDefault(
866866
}
867867
}
868868

869+
@MethodHook(
870+
type = HookType.BEFORE,
871+
targetClassName = "java.lang.Enum",
872+
targetMethod = "valueOf",
873+
targetMethodDescriptor = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;")
874+
public static void enumValueOf(
875+
MethodHandle method, Object alwaysNull, Object[] arguments, int hookId) {
876+
if (arguments.length < 2) {
877+
return;
878+
}
879+
Object enumTypeArg = arguments[0];
880+
Object nameArg = arguments[1];
881+
if (!(enumTypeArg instanceof Class) || !(nameArg instanceof String)) {
882+
return;
883+
}
884+
Class<?> enumClass = (Class<?>) enumTypeArg;
885+
if (enumClass == null || !enumClass.isEnum()) {
886+
return;
887+
}
888+
String candidate = (String) nameArg;
889+
if (candidate == null) {
890+
return;
891+
}
892+
893+
Enum<?>[] constants;
894+
try {
895+
constants = (Enum<?>[]) enumClass.getEnumConstants();
896+
} catch (Exception | LinkageError ignored) {
897+
return;
898+
}
899+
if (constants == null || constants.length == 0) {
900+
return;
901+
}
902+
903+
// Skip guidance if the target string is already valid.
904+
for (Enum<?> enumConstant : constants) {
905+
if (enumConstant.name().equals(candidate)) {
906+
return;
907+
}
908+
}
909+
910+
// Guide the fuzzer towards a single valid enum
911+
int index = Math.floorMod(candidate.hashCode(), constants.length);
912+
Enum<?> target = constants[index];
913+
TraceDataFlowNativeCallbacks.traceStrcmp(candidate, target.name(), 1, hookId);
914+
}
915+
869916
private static final class Bounds {
870917
private final Object lower;
871918
private final Object upper;

tests/BUILD.bazel

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ java_fuzz_target_test(
701701
srcs = ["src/test/java/com/example/SetFuzzer.java"],
702702
allowed_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium"],
703703
fuzzer_args = [
704-
"-runs=1000000",
704+
"-runs=100000",
705705
],
706706
target_class = "com.example.SetFuzzer",
707707
verify_crash_reproducer = False,
@@ -710,6 +710,20 @@ java_fuzz_target_test(
710710
],
711711
)
712712

713+
java_fuzz_target_test(
714+
name = "EnumFuzzer",
715+
srcs = ["src/test/java/com/example/EnumFuzzer.java"],
716+
allowed_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium"],
717+
fuzzer_args = [
718+
"-runs=100000",
719+
],
720+
target_class = "com.example.EnumFuzzer",
721+
verify_crash_reproducer = False,
722+
deps = [
723+
"//src/main/java/com/code_intelligence/jazzer/mutation/annotation",
724+
],
725+
)
726+
713727
sh_test(
714728
name = "jazzer_from_path_test",
715729
srcs = ["src/test/shell/jazzer_from_path_test.sh"],
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2024 Code Intelligence GmbH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example;
18+
19+
import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium;
20+
import com.code_intelligence.jazzer.mutation.annotation.NotNull;
21+
22+
public class EnumFuzzer {
23+
private enum SampleColor {
24+
RED,
25+
GREEN,
26+
BLUE,
27+
SUPER_SECRET_SHADE
28+
}
29+
30+
public static void fuzzerTestOneInput(@NotNull String value) {
31+
try {
32+
SampleColor color = SampleColor.valueOf(value);
33+
if (color == SampleColor.SUPER_SECRET_SHADE) {
34+
throw new FuzzerSecurityIssueMedium("Enum matched: " + color);
35+
}
36+
} catch (IllegalArgumentException ignored) {
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)