Skip to content

Commit 0f295f0

Browse files
committed
runtime: Instrument Map#getOrDefault and Map#containsKey
1 parent 52863e8 commit 0f295f0

File tree

3 files changed

+85
-11
lines changed

3 files changed

+85
-11
lines changed

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

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -756,18 +756,46 @@ public static void arraysCompareRange(
756756
// key closest to the current lookup key in the mapGet hook.
757757
private static final int MAX_NUM_KEYS_TO_ENUMERATE = 100;
758758

759-
@SuppressWarnings({"rawtypes", "unchecked"})
760-
@MethodHook(type = HookType.AFTER, targetClassName = "java.util.Map", targetMethod = "get")
759+
@MethodHook(
760+
type = HookType.AFTER,
761+
targetClassName = "java.util.Map",
762+
targetMethod = "containsKey",
763+
targetMethodDescriptor = "(Ljava/lang/Object;)Z")
764+
public static void containsKey(
765+
MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean isContained) {
766+
if (!isContained) {
767+
mapHookInternal((Map) thisObject, arguments[0], hookId);
768+
}
769+
}
770+
771+
@MethodHook(
772+
type = HookType.AFTER,
773+
targetClassName = "java.util.Map",
774+
targetMethod = "get",
775+
targetMethodDescriptor = "(Ljava/lang/Object;)Ljava/lang/Object;")
761776
public static void mapGet(
762-
MethodHandle method, Object thisObject, Object[] arguments, int hookId, Object returnValue) {
763-
if (returnValue != null) return;
764-
if (arguments.length != 1) {
765-
return;
777+
MethodHandle method, Object thisObject, Object[] arguments, int hookId, Object value) {
778+
if (value == null) {
779+
mapHookInternal((Map) thisObject, arguments[0], hookId);
766780
}
767-
if (thisObject == null) return;
768-
final Map map = (Map) thisObject;
769-
if (map.size() == 0) return;
770-
final Object currentKey = arguments[0];
781+
}
782+
783+
@MethodHook(
784+
type = HookType.AFTER,
785+
targetClassName = "java.util.Map",
786+
targetMethod = "getOrDefault",
787+
targetMethodDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")
788+
public static void mapGetOrDefault(
789+
MethodHandle method, Object thisObject, Object[] arguments, int hookId, Object value) {
790+
Object defaultValue = arguments[1];
791+
if (value == defaultValue) {
792+
mapHookInternal((Map) thisObject, arguments[0], hookId);
793+
}
794+
}
795+
796+
@SuppressWarnings({"rawtypes", "unchecked"})
797+
private static <K, V> void mapHookInternal(Map<K, V> map, K currentKey, int hookId) {
798+
if (map == null || map.isEmpty()) return;
771799
if (currentKey == null) return;
772800
// Find two valid map keys that bracket currentKey.
773801
// This is a generalization of libFuzzer's __sanitizer_cov_trace_switch:
@@ -776,7 +804,7 @@ public static void mapGet(
776804
Object upperBoundKey = null;
777805
try {
778806
if (map instanceof TreeMap) {
779-
final TreeMap treeMap = (TreeMap) map;
807+
final TreeMap<K, V> treeMap = (TreeMap<K, V>) map;
780808
try {
781809
lowerBoundKey = treeMap.floorKey(currentKey);
782810
upperBoundKey = treeMap.ceilingKey(currentKey);

tests/BUILD.bazel

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,21 @@ java_fuzz_target_test(
576576
],
577577
)
578578

579+
java_fuzz_target_test(
580+
name = "MapFuzzer",
581+
srcs = ["src/test/java/com/example/MapFuzzer.java"],
582+
allowed_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium"],
583+
fuzzer_args = [
584+
"--experimental_mutator",
585+
"-use_value_profile=1",
586+
],
587+
target_class = "com.example.MapFuzzer",
588+
verify_crash_reproducer = False,
589+
deps = [
590+
"//src/main/java/com/code_intelligence/jazzer/mutation/annotation",
591+
],
592+
)
593+
579594
sh_test(
580595
name = "jazzer_from_path_test",
581596
srcs = ["src/test/shell/jazzer_from_path_test.sh"],
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2023 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+
import java.util.Map;
22+
23+
public class MapFuzzer {
24+
public static void fuzzerTestOneInput(@NotNull Map<@NotNull String, @NotNull String> map) {
25+
if (map.getOrDefault("some_key", "").startsWith("prefix")) {
26+
if (map.containsKey("other_key")) {
27+
throw new FuzzerSecurityIssueMedium();
28+
}
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)