aboutsummaryrefslogtreecommitdiff
path: root/agent/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'agent/src/test')
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/api/AutofuzzTest.java1
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/api/BUILD.bazel1
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/autofuzz/MetaTest.java60
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/autofuzz/TestHelpers.java4
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/AfterHooksPatchTest.kt5
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/BUILD.bazel2
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/BeforeHooksPatchTest.kt5
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/CoverageInstrumentationTest.kt112
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/HookValidationTest.kt6
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/InvalidHookMocks.java28
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/MockCoverageMap.java18
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/PatchTestUtils.kt52
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooks.java27
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksInit.java26
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksPatchTest.kt5
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksTarget.java10
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentationTarget.java1
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentationTest.kt2
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ValidHookMocks.java4
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/runtime/BUILD.bazel38
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/runtime/FuzzedDataProviderImplTest.java225
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/runtime/RecordingFuzzedDataProviderTest.java214
-rw-r--r--agent/src/test/java/com/code_intelligence/jazzer/runtime/TraceCmpHooksTest.java54
23 files changed, 811 insertions, 89 deletions
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/api/AutofuzzTest.java b/agent/src/test/java/com/code_intelligence/jazzer/api/AutofuzzTest.java
index 66a85db6..59ef238d 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/api/AutofuzzTest.java
+++ b/agent/src/test/java/com/code_intelligence/jazzer/api/AutofuzzTest.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.Collections;
+import org.junit.BeforeClass;
import org.junit.Test;
public class AutofuzzTest {
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/api/BUILD.bazel b/agent/src/test/java/com/code_intelligence/jazzer/api/BUILD.bazel
index 9192ff77..f2537b73 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/api/BUILD.bazel
+++ b/agent/src/test/java/com/code_intelligence/jazzer/api/BUILD.bazel
@@ -16,6 +16,7 @@ java_test(
],
deps = [
"//agent/src/main/java/com/code_intelligence/jazzer/api",
+ "//driver/src/main/native/com/code_intelligence/jazzer/driver:jazzer_driver",
"@maven//:junit_junit",
],
)
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/MetaTest.java b/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/MetaTest.java
index 0615e9ae..0906d1d5 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/MetaTest.java
+++ b/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/MetaTest.java
@@ -22,19 +22,13 @@ import com.code_intelligence.jazzer.api.CannedFuzzedDataProvider;
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.google.json.JsonSanitizer;
import java.io.ByteArrayInputStream;
+import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Map;
import org.junit.Test;
public class MetaTest {
- public static boolean isFive(int arg) {
- return arg == 5;
- }
-
- public static boolean intEquals(int arg1, int arg2) {
- return arg1 == arg2;
- }
-
public enum TestEnum {
FOO,
BAR,
@@ -42,7 +36,7 @@ public class MetaTest {
}
@Test
- public void testConsume() {
+ public void testConsume() throws NoSuchMethodException {
consumeTestCase(5, "5", Collections.singletonList(5));
consumeTestCase((short) 5, "(short) 5", Collections.singletonList((short) 5));
consumeTestCase(5L, "5L", Collections.singletonList(5L));
@@ -121,6 +115,52 @@ public class MetaTest {
consumeTestCase(YourAverageJavaClass.class,
"com.code_intelligence.jazzer.autofuzz.YourAverageJavaClass.class",
Collections.singletonList((byte) 1));
+
+ Type stringStringMapType =
+ MetaTest.class.getDeclaredMethod("returnsStringStringMap").getGenericReturnType();
+ Map<String, String> expectedMap =
+ java.util.stream.Stream
+ .of(new java.util.AbstractMap.SimpleEntry<>("key0", "value0"),
+ new java.util.AbstractMap.SimpleEntry<>("key1", "value1"),
+ new java.util.AbstractMap.SimpleEntry<>("key2", (java.lang.String) null))
+ .collect(java.util.HashMap::new,
+ (map, e) -> map.put(e.getKey(), e.getValue()), java.util.HashMap::putAll);
+ consumeTestCase(stringStringMapType, expectedMap,
+ "java.util.stream.Stream.<java.util.AbstractMap.SimpleEntry<java.lang.String, java.lang.String>>of(new java.util.AbstractMap.SimpleEntry<>(\"key0\", \"value0\"), new java.util.AbstractMap.SimpleEntry<>(\"key1\", \"value1\"), new java.util.AbstractMap.SimpleEntry<>(\"key2\", (java.lang.String) null)).collect(java.util.HashMap::new, (map, e) -> map.put(e.getKey(), e.getValue()), java.util.HashMap::putAll)",
+ Arrays.asList((byte) 1, // do not return null for the map
+ 32, // remaining bytes
+ (byte) 1, // do not return null for the string
+ 31, // remaining bytes
+ "key0",
+ (byte) 1, // do not return null for the string
+ 28, // remaining bytes
+ "value0",
+ 28, // remaining bytes
+ 28, // consumeArrayLength
+ (byte) 1, // do not return null for the string
+ 27, // remaining bytes
+ "key1",
+ (byte) 1, // do not return null for the string
+ 23, // remaining bytes
+ "value1",
+ (byte) 1, // do not return null for the string
+ 27, // remaining bytes
+ "key2",
+ (byte) 0 // *do* return null for the string
+ ));
+ }
+
+ private Map<String, String> returnsStringStringMap() {
+ throw new IllegalStateException(
+ "Should not be called, only exists to construct its generic return type");
+ }
+
+ public static boolean isFive(int arg) {
+ return arg == 5;
+ }
+
+ public static boolean intEquals(int arg1, int arg2) {
+ return arg1 == arg2;
}
@Test
@@ -129,7 +169,7 @@ public class MetaTest {
MetaTest.class.getMethod("isFive", int.class), Collections.singletonList(5));
autofuzzTestCase(false, "com.code_intelligence.jazzer.autofuzz.MetaTest.intEquals(5, 4)",
MetaTest.class.getMethod("intEquals", int.class, int.class), Arrays.asList(5, 4));
- autofuzzTestCase("foobar", "\"foo\".concat(\"bar\")",
+ autofuzzTestCase("foobar", "(\"foo\").concat(\"bar\")",
String.class.getMethod("concat", String.class),
Arrays.asList((byte) 1, 6, "foo", (byte) 1, 6, "bar"));
autofuzzTestCase("jazzer", "new java.lang.String(\"jazzer\")",
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/TestHelpers.java b/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/TestHelpers.java
index 52f19a74..d556beb3 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/TestHelpers.java
+++ b/agent/src/test/java/com/code_intelligence/jazzer/autofuzz/TestHelpers.java
@@ -24,6 +24,7 @@ import java.io.ByteArrayInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
+import java.lang.reflect.Type;
import java.util.List;
class TestHelpers {
@@ -57,8 +58,7 @@ class TestHelpers {
}
static void consumeTestCase(
- Class<?> type, Object expectedResult, String expectedResultString, List<Object> cannedData) {
- assertTrue(expectedResult == null || type.isAssignableFrom(expectedResult.getClass()));
+ Type type, Object expectedResult, String expectedResultString, List<Object> cannedData) {
AutofuzzCodegenVisitor visitor = new AutofuzzCodegenVisitor();
FuzzedDataProvider data = CannedFuzzedDataProvider.create(cannedData);
assertGeneralEquals(expectedResult, Meta.consume(data, type, visitor));
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/AfterHooksPatchTest.kt b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/AfterHooksPatchTest.kt
index 53efd200..c5a2e156 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/AfterHooksPatchTest.kt
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/AfterHooksPatchTest.kt
@@ -14,11 +14,14 @@
package com.code_intelligence.jazzer.instrumentor
+import com.code_intelligence.jazzer.instrumentor.PatchTestUtils.bytecodeToClass
+import com.code_intelligence.jazzer.instrumentor.PatchTestUtils.classToBytecode
import org.junit.Test
import java.io.File
private fun applyAfterHooks(bytecode: ByteArray): ByteArray {
- return HookInstrumentor(loadHooks(AfterHooks::class.java), false).instrument(bytecode)
+ val hooks = Hooks.loadHooks(setOf(AfterHooks::class.java.name)).first().hooks
+ return HookInstrumentor(hooks, false).instrument(bytecode)
}
private fun getOriginalAfterHooksTargetInstance(): AfterHooksTargetContract {
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/BUILD.bazel b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/BUILD.bazel
index 472d2b98..036559ec 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/BUILD.bazel
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/BUILD.bazel
@@ -7,6 +7,7 @@ kt_jvm_library(
"DynamicTestContract.java",
"PatchTestUtils.kt",
],
+ visibility = ["//visibility:public"],
)
wrapped_kt_jvm_test(
@@ -130,6 +131,7 @@ wrapped_kt_jvm_test(
size = "small",
srcs = [
"ReplaceHooks.java",
+ "ReplaceHooksInit.java",
"ReplaceHooksPatchTest.kt",
"ReplaceHooksTarget.java",
"ReplaceHooksTargetContract.java",
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/BeforeHooksPatchTest.kt b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/BeforeHooksPatchTest.kt
index 31e9733c..4fde7ee1 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/BeforeHooksPatchTest.kt
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/BeforeHooksPatchTest.kt
@@ -14,11 +14,14 @@
package com.code_intelligence.jazzer.instrumentor
+import com.code_intelligence.jazzer.instrumentor.PatchTestUtils.bytecodeToClass
+import com.code_intelligence.jazzer.instrumentor.PatchTestUtils.classToBytecode
import org.junit.Test
import java.io.File
private fun applyBeforeHooks(bytecode: ByteArray): ByteArray {
- return HookInstrumentor(loadHooks(BeforeHooks::class.java), false).instrument(bytecode)
+ val hooks = Hooks.loadHooks(setOf(BeforeHooks::class.java.name)).first().hooks
+ return HookInstrumentor(hooks, false).instrument(bytecode)
}
private fun getOriginalBeforeHooksTargetInstance(): BeforeHooksTargetContract {
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/CoverageInstrumentationTest.kt b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/CoverageInstrumentationTest.kt
index 15c88f4c..f2cf2f08 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/CoverageInstrumentationTest.kt
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/CoverageInstrumentationTest.kt
@@ -14,12 +14,37 @@
package com.code_intelligence.jazzer.instrumentor
+import com.code_intelligence.jazzer.instrumentor.PatchTestUtils.bytecodeToClass
+import com.code_intelligence.jazzer.instrumentor.PatchTestUtils.classToBytecode
import org.junit.Test
+import org.objectweb.asm.MethodVisitor
+import org.objectweb.asm.Opcodes
import java.io.File
import kotlin.test.assertEquals
+/**
+ * Amends the instrumentation performed by [strategy] to call the map's public static void method
+ * updated() after every update to coverage counters.
+ */
+private fun makeTestable(strategy: EdgeCoverageStrategy): EdgeCoverageStrategy =
+ object : EdgeCoverageStrategy by strategy {
+ override fun instrumentControlFlowEdge(
+ mv: MethodVisitor,
+ edgeId: Int,
+ variable: Int,
+ coverageMapInternalClassName: String
+ ) {
+ strategy.instrumentControlFlowEdge(mv, edgeId, variable, coverageMapInternalClassName)
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, coverageMapInternalClassName, "updated", "()V", false)
+ }
+ }
+
private fun applyInstrumentation(bytecode: ByteArray): ByteArray {
- return EdgeCoverageInstrumentor(0, MockCoverageMap::class.java).instrument(bytecode)
+ return EdgeCoverageInstrumentor(
+ makeTestable(ClassInstrumentor.defaultEdgeCoverageStrategy),
+ MockCoverageMap::class.java,
+ 0
+ ).instrument(bytecode)
}
private fun getOriginalInstrumentationTargetInstance(): DynamicTestContract {
@@ -41,26 +66,34 @@ private fun assertControlFlow(expectedLocations: List<Int>) {
assertEquals(expectedLocations, MockCoverageMap.locations.toList())
}
+@Suppress("unused")
class CoverageInstrumentationTest {
private val constructorReturn = 0
- private val ifFirstBranch = 1
- @Suppress("unused")
- private val ifSecondBranch = 2
- private val ifEnd = 3
- private val outerForCondition = 4
- private val innerForBodyIfFirstRun = 6
- private val innerForBodyIfSecondRun = 5
- private val innerForIncrementCounter = 7
- private val outerForIncrementCounter = 8
- private val afterFooInvocation = 9
- private val beforeReturn = 10
- private val fooAfterBarInvocation = 11
- private val fooBeforeReturn = 12
- private val barAfterMapPutInvocation = 13
- private val barBeforeReturn = 14
- @Suppress("unused")
- private val bazReturn = 15
+
+ private val mapConstructor = 1
+ private val addFor0 = 2
+ private val addFor1 = 3
+ private val addFor2 = 4
+ private val addFor3 = 5
+ private val addFor4 = 6
+ private val addFoobar = 7
+
+ private val ifTrueBranch = 8
+ private val addBlock1 = 9
+ private val ifFalseBranch = 10
+ private val ifEnd = 11
+
+ private val outerForCondition = 12
+ private val innerForCondition = 13
+ private val innerForBodyIfTrueBranch = 14
+ private val innerForBodyIfFalseBranch = 15
+ private val innerForBodyPutInvocation = 16
+ private val outerForIncrementCounter = 17
+
+ private val afterFooInvocation = 18
+ private val fooAfterBarInvocation = 19
+ private val barAfterPutInvocation = 20
@Test
fun testOriginal() {
@@ -72,31 +105,32 @@ class CoverageInstrumentationTest {
MockCoverageMap.clear()
assertSelfCheck(getInstrumentedInstrumentationTargetInstance())
- val innerForFirstRunControlFlow = mutableListOf<Int>().apply {
+ val mapControlFlow = listOf(mapConstructor, addFor0, addFor1, addFor2, addFor3, addFor4, addFoobar)
+ val ifControlFlow = listOf(ifTrueBranch, addBlock1, ifEnd)
+ val forFirstRunControlFlow = mutableListOf<Int>().apply {
+ add(outerForCondition)
repeat(5) {
- addAll(listOf(innerForBodyIfFirstRun, innerForIncrementCounter))
+ addAll(listOf(innerForCondition, innerForBodyIfFalseBranch, innerForBodyPutInvocation))
}
+ add(outerForIncrementCounter)
}.toList()
- val innerForSecondRunControlFlow = mutableListOf<Int>().apply {
+ val forSecondRunControlFlow = mutableListOf<Int>().apply {
+ add(outerForCondition)
repeat(5) {
- addAll(listOf(innerForBodyIfSecondRun, innerForIncrementCounter))
+ addAll(listOf(innerForCondition, innerForBodyIfTrueBranch, innerForBodyPutInvocation))
}
+ add(outerForIncrementCounter)
}.toList()
- val outerForControlFlow =
- listOf(outerForCondition) +
- innerForFirstRunControlFlow +
- listOf(outerForIncrementCounter, outerForCondition) +
- innerForSecondRunControlFlow +
- listOf(outerForIncrementCounter)
-
+ val forControlFlow = forFirstRunControlFlow + forSecondRunControlFlow
+ val fooCallControlFlow = listOf(
+ barAfterPutInvocation, fooAfterBarInvocation, afterFooInvocation
+ )
assertControlFlow(
- listOf(constructorReturn, ifFirstBranch, ifEnd) +
- outerForControlFlow +
- listOf(
- barAfterMapPutInvocation, barBeforeReturn,
- fooAfterBarInvocation, fooBeforeReturn,
- afterFooInvocation, beforeReturn
- )
+ listOf(constructorReturn) +
+ mapControlFlow +
+ ifControlFlow +
+ forControlFlow +
+ fooCallControlFlow
)
}
@@ -109,17 +143,17 @@ class CoverageInstrumentationTest {
// The constructor of the target is run only once.
val takenOnceEdge = constructorReturn
// Control flows through the first if branch once per run.
- val takenOnEveryRunEdge = ifFirstBranch
+ val takenOnEveryRunEdge = ifTrueBranch
var lastCounter = 0.toUByte()
for (i in 1..600) {
assertSelfCheck(target)
- assertEquals(1, MockCoverageMap.mem[takenOnceEdge])
+ assertEquals(1, MockCoverageMap.counters[takenOnceEdge])
// Verify that the counter increments, but is never zero.
val expectedCounter = (lastCounter + 1U).toUByte().takeUnless { it == 0.toUByte() }
?: (lastCounter + 2U).toUByte()
lastCounter = expectedCounter
- val actualCounter = MockCoverageMap.mem[takenOnEveryRunEdge].toUByte()
+ val actualCounter = MockCoverageMap.counters[takenOnEveryRunEdge].toUByte()
assertEquals(expectedCounter, actualCounter, "After $i runs:")
}
}
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/HookValidationTest.kt b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/HookValidationTest.kt
index 7e7c31c9..ac263dc5 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/HookValidationTest.kt
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/HookValidationTest.kt
@@ -22,7 +22,8 @@ import kotlin.test.assertFailsWith
class HookValidationTest {
@Test
fun testValidHooks() {
- assertEquals(6, loadHooks(ValidHookMocks::class.java).size)
+ val hooks = Hooks.loadHooks(setOf(ValidHookMocks::class.java.name)).first().hooks
+ assertEquals(5, hooks.size)
}
@Test
@@ -30,7 +31,8 @@ class HookValidationTest {
for (method in InvalidHookMocks::class.java.methods) {
if (method.isAnnotationPresent(MethodHook::class.java)) {
assertFailsWith<IllegalArgumentException>("Expected ${method.name} to be an invalid hook") {
- Hook.verifyAndGetHook(method, method.declaredAnnotations.first() as MethodHook)
+ val methodHook = method.declaredAnnotations.first() as MethodHook
+ Hook.createAndVerifyHook(method, methodHook, methodHook.targetClassName)
}
}
}
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/InvalidHookMocks.java b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/InvalidHookMocks.java
index 2723ad6e..0df349ca 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/InvalidHookMocks.java
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/InvalidHookMocks.java
@@ -18,6 +18,7 @@ import com.code_intelligence.jazzer.api.HookType;
import com.code_intelligence.jazzer.api.MethodHook;
import java.lang.invoke.MethodHandle;
+@SuppressWarnings({"unused", "RedundantThrows"})
class InvalidHookMocks {
@MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.String", targetMethod = "equals")
public static void incorrectHookIdType(
@@ -45,7 +46,14 @@ class InvalidHookMocks {
return true;
}
- @MethodHook(type = HookType.REPLACE, targetClassName = "java.lang.StringBuilder",
+ @MethodHook(type = HookType.REPLACE, targetClassName = "java.lang.System", targetMethod = "gc",
+ targetMethodDescriptor = "()V")
+ public static Object
+ invalidReplaceVoidMethod(MethodHandle method, Object thisObject, Object[] arguments, int hookId) {
+ return null;
+ }
+
+ @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.StringBuilder",
targetMethod = "<init>", targetMethodDescriptor = "(Ljava/lang/String;)V")
public static Object
invalidReturnType(MethodHandle method, Object thisObject, Object[] arguments, int hookId)
@@ -58,4 +66,22 @@ class InvalidHookMocks {
public static void
primitiveReturnValueMustBeWrapped(MethodHandle method, String thisObject, Object[] arguments,
int hookId, boolean returnValue) {}
+
+ @MethodHook(type = HookType.REPLACE, targetClassName = "java.lang.StringBuilder",
+ targetMethod = "<init>", targetMethodDescriptor = "(Ljava/lang/String;)V")
+ public static void
+ replaceOnInitWithoutReturnType(
+ MethodHandle method, Object thisObject, Object[] arguments, int hookId) throws Throwable {}
+
+ @MethodHook(type = HookType.REPLACE, targetClassName = "java.lang.StringBuilder",
+ targetMethod = "<init>", targetMethodDescriptor = "(Ljava/lang/String;)V")
+ public static Object
+ replaceOnInitWithIncompatibleType(
+ MethodHandle method, Object thisObject, Object[] arguments, int hookId) throws Throwable {
+ return new Object();
+ }
+
+ @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "equals")
+ public static void primitiveReturnType(MethodHandle method, String thisObject, Object[] arguments,
+ int hookId, boolean returnValue) {}
}
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/MockCoverageMap.java b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/MockCoverageMap.java
index 787ea493..3ea33d19 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/MockCoverageMap.java
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/MockCoverageMap.java
@@ -20,8 +20,7 @@ import java.util.Arrays;
public class MockCoverageMap {
public static final int SIZE = 65536;
- public static final ByteBuffer mem = ByteBuffer.allocate(SIZE);
- public static int prev_location = 0; // is used in byte code directly
+ public static final ByteBuffer counters = ByteBuffer.allocate(SIZE);
private static final ByteBuffer previous_mem = ByteBuffer.allocate(SIZE);
public static ArrayList<Integer> locations = new ArrayList<>();
@@ -29,16 +28,25 @@ public class MockCoverageMap {
public static void updated() {
int updated_pos = -1;
for (int i = 0; i < SIZE; i++) {
- if (previous_mem.get(i) != mem.get(i)) {
+ if (previous_mem.get(i) != counters.get(i)) {
updated_pos = i;
}
}
locations.add(updated_pos);
- System.arraycopy(mem.array(), 0, previous_mem.array(), 0, SIZE);
+ System.arraycopy(counters.array(), 0, previous_mem.array(), 0, SIZE);
+ }
+
+ public static void enlargeIfNeeded(int nextId) {
+ // This mock coverage map is statically sized.
+ }
+
+ public static void recordCoverage(int id) {
+ byte counter = counters.get(id);
+ counters.put(id, (byte) (counter == -1 ? 1 : counter + 1));
}
public static void clear() {
- Arrays.fill(mem.array(), (byte) 0);
+ Arrays.fill(counters.array(), (byte) 0);
Arrays.fill(previous_mem.array(), (byte) 0);
locations.clear();
}
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/PatchTestUtils.kt b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/PatchTestUtils.kt
index f286d03f..00279c35 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/PatchTestUtils.kt
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/PatchTestUtils.kt
@@ -14,30 +14,40 @@
package com.code_intelligence.jazzer.instrumentor
-fun classToBytecode(targetClass: Class<*>): ByteArray {
- return ClassLoader
- .getSystemClassLoader()
- .getResourceAsStream("${targetClass.name.replace('.', '/')}.class")!!
- .use {
- it.readBytes()
- }
-}
+import java.io.FileOutputStream
-fun bytecodeToClass(name: String, bytecode: ByteArray): Class<*> {
- return BytecodeClassLoader(name, bytecode).loadClass(name)
-}
+object PatchTestUtils {
+ @JvmStatic
+ fun classToBytecode(targetClass: Class<*>): ByteArray {
+ return ClassLoader
+ .getSystemClassLoader()
+ .getResourceAsStream("${targetClass.name.replace('.', '/')}.class")!!
+ .use {
+ it.readBytes()
+ }
+ }
-/**
- * A ClassLoader that dynamically loads a single specified class from byte code and delegates all other class loads to
- * its own ClassLoader.
- */
-class BytecodeClassLoader(val className: String, private val classBytecode: ByteArray) :
- ClassLoader(BytecodeClassLoader::class.java.classLoader) {
- override fun loadClass(name: String): Class<*> {
- if (name != className)
- return super.loadClass(name)
+ @JvmStatic
+ fun bytecodeToClass(name: String, bytecode: ByteArray): Class<*> {
+ return BytecodeClassLoader(name, bytecode).loadClass(name)
+ }
+
+ @JvmStatic
+ public fun dumpBytecode(outDir: String, name: String, originalBytecode: ByteArray) {
+ FileOutputStream("$outDir/$name.class").use { fos -> fos.write(originalBytecode) }
+ }
- return defineClass(className, classBytecode, 0, classBytecode.size)
+ /**
+ * A ClassLoader that dynamically loads a single specified class from byte code and delegates all other class loads to
+ * its own ClassLoader.
+ */
+ class BytecodeClassLoader(val className: String, private val classBytecode: ByteArray) :
+ ClassLoader(BytecodeClassLoader::class.java.classLoader) {
+ override fun loadClass(name: String): Class<*> {
+ if (name != className)
+ return super.loadClass(name)
+ return defineClass(className, classBytecode, 0, classBytecode.size)
+ }
}
}
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooks.java b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooks.java
index a71e1180..7e31b77b 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooks.java
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooks.java
@@ -18,6 +18,7 @@ import com.code_intelligence.jazzer.api.HookType;
import com.code_intelligence.jazzer.api.MethodHook;
import java.lang.invoke.MethodHandle;
+@SuppressWarnings("unused")
public class ReplaceHooks {
@MethodHook(type = HookType.REPLACE,
targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksTarget",
@@ -106,4 +107,30 @@ public class ReplaceHooks {
patchAbstractListGet(MethodHandle method, Object thisObject, Object[] arguments, int hookId) {
return true;
}
+
+ @MethodHook(type = HookType.REPLACE, targetClassName = "java.util.Set", targetMethod = "contains",
+ targetMethodDescriptor = "(Ljava/lang/Object;)Z")
+ public static boolean
+ patchSetGet(MethodHandle method, Object thisObject, Object[] arguments, int hookId) {
+ return true;
+ }
+
+ @MethodHook(type = HookType.REPLACE,
+ targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksInit",
+ targetMethod = "<init>", targetMethodDescriptor = "()V")
+ public static ReplaceHooksInit
+ patchInit(MethodHandle method, Object thisObject, Object[] arguments, int hookId) {
+ // Test with subclass
+ return new ReplaceHooksInit() {
+ { initialized = true; }
+ };
+ }
+
+ @MethodHook(type = HookType.REPLACE,
+ targetClassName = "com.code_intelligence.jazzer.instrumentor.ReplaceHooksInit",
+ targetMethod = "<init>", targetMethodDescriptor = "(ZLjava/lang/String;)V")
+ public static ReplaceHooksInit
+ patchInitWithParams(MethodHandle method, Object thisObject, Object[] arguments, int hookId) {
+ return new ReplaceHooksInit(true, "");
+ }
}
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksInit.java b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksInit.java
new file mode 100644
index 00000000..da77be81
--- /dev/null
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksInit.java
@@ -0,0 +1,26 @@
+// Copyright 2021 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.code_intelligence.jazzer.instrumentor;
+
+public class ReplaceHooksInit {
+ public boolean initialized;
+
+ public ReplaceHooksInit() {}
+
+ @SuppressWarnings("unused")
+ public ReplaceHooksInit(boolean initialized, String ignored) {
+ this.initialized = initialized;
+ }
+}
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksPatchTest.kt b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksPatchTest.kt
index 76fb53e5..b6266d12 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksPatchTest.kt
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksPatchTest.kt
@@ -14,11 +14,14 @@
package com.code_intelligence.jazzer.instrumentor
+import com.code_intelligence.jazzer.instrumentor.PatchTestUtils.bytecodeToClass
+import com.code_intelligence.jazzer.instrumentor.PatchTestUtils.classToBytecode
import org.junit.Test
import java.io.File
private fun applyReplaceHooks(bytecode: ByteArray): ByteArray {
- return HookInstrumentor(loadHooks(ReplaceHooks::class.java), false).instrument(bytecode)
+ val hooks = Hooks.loadHooks(setOf(ReplaceHooks::class.java.name)).first().hooks
+ return HookInstrumentor(hooks, false).instrument(bytecode)
}
private fun getOriginalReplaceHooksTargetInstance(): ReplaceHooksTargetContract {
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksTarget.java b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksTarget.java
index 7a4b89f8..fadbdf80 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksTarget.java
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ReplaceHooksTarget.java
@@ -15,9 +15,9 @@
package com.code_intelligence.jazzer.instrumentor;
import java.security.SecureRandom;
-import java.util.AbstractList;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
// selfCheck() only passes with the hooks in ReplaceHooks.java applied.
@@ -56,10 +56,16 @@ public class ReplaceHooksTarget implements ReplaceHooksTargetContract {
shouldCallPass();
}
- AbstractList<Boolean> boolList = new ArrayList<>();
+ ArrayList<Boolean> boolList = new ArrayList<>();
boolList.add(false);
results.put("arrayListGet", boolList.get(0));
+ HashSet<Boolean> boolSet = new HashSet<>();
+ results.put("stringSetGet", boolSet.contains(Boolean.TRUE));
+
+ results.put("shouldInitialize", new ReplaceHooksInit().initialized);
+ results.put("shouldInitializeWithParams", new ReplaceHooksInit(false, "foo").initialized);
+
return results;
}
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentationTarget.java b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentationTarget.java
index 48f16e60..d8e28881 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentationTarget.java
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentationTarget.java
@@ -37,6 +37,7 @@ public class TraceDataFlowInstrumentationTarget implements DynamicTestContract {
volatile int switchValue = 1200;
+ @SuppressWarnings("ReturnValueIgnored")
@Override
public Map<String, Boolean> selfCheck() {
Map<String, Boolean> results = new HashMap<>();
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentationTest.kt b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentationTest.kt
index c6fd218f..4d4b0318 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentationTest.kt
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentationTest.kt
@@ -14,6 +14,8 @@
package com.code_intelligence.jazzer.instrumentor
+import com.code_intelligence.jazzer.instrumentor.PatchTestUtils.bytecodeToClass
+import com.code_intelligence.jazzer.instrumentor.PatchTestUtils.classToBytecode
import org.junit.Test
import java.io.File
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ValidHookMocks.java b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ValidHookMocks.java
index 06bed141..a919242b 100644
--- a/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ValidHookMocks.java
+++ b/agent/src/test/java/com/code_intelligence/jazzer/instrumentor/ValidHookMocks.java
@@ -27,10 +27,6 @@ class ValidHookMocks {
public static void validAfterHook(MethodHandle method, String thisObject, Object[] arguments,
int hookId, Boolean returnValue) {}
- @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "equals")
- public static void validAfterHook2(MethodHandle method, String thisObject, Object[] arguments,
- int hookId, boolean returnValue) {}
-
@MethodHook(type = HookType.REPLACE, targetClassName = "java.lang.String",
targetMethod = "equals", targetMethodDescriptor = "(Ljava/lang/Object;)Z")
public static Boolean
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/runtime/BUILD.bazel b/agent/src/test/java/com/code_intelligence/jazzer/runtime/BUILD.bazel
new file mode 100644
index 00000000..97ac4f62
--- /dev/null
+++ b/agent/src/test/java/com/code_intelligence/jazzer/runtime/BUILD.bazel
@@ -0,0 +1,38 @@
+load("//bazel:compat.bzl", "SKIP_ON_WINDOWS")
+
+java_test(
+ name = "FuzzedDataProviderImplTest",
+ srcs = ["FuzzedDataProviderImplTest.java"],
+ use_testrunner = False,
+ deps = [
+ "//agent/src/main/java/com/code_intelligence/jazzer/api",
+ "//agent/src/main/java/com/code_intelligence/jazzer/runtime:fuzzed_data_provider",
+ "//driver/src/main/native/com/code_intelligence/jazzer/driver:jazzer_driver",
+ ],
+)
+
+java_test(
+ name = "RecordingFuzzedDataProviderTest",
+ srcs = [
+ "RecordingFuzzedDataProviderTest.java",
+ ],
+ deps = [
+ "//agent/src/main/java/com/code_intelligence/jazzer/api",
+ "//agent/src/main/java/com/code_intelligence/jazzer/runtime",
+ "//agent/src/main/java/com/code_intelligence/jazzer/runtime:fuzzed_data_provider",
+ "@maven//:junit_junit",
+ ],
+)
+
+java_test(
+ name = "TraceCmpHooksTest",
+ srcs = [
+ "TraceCmpHooksTest.java",
+ ],
+ target_compatible_with = SKIP_ON_WINDOWS,
+ deps = [
+ "//agent/src/main/java/com/code_intelligence/jazzer/runtime",
+ "//driver/src/main/native/com/code_intelligence/jazzer/driver:jazzer_driver",
+ "@maven//:junit_junit",
+ ],
+)
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/runtime/FuzzedDataProviderImplTest.java b/agent/src/test/java/com/code_intelligence/jazzer/runtime/FuzzedDataProviderImplTest.java
new file mode 100644
index 00000000..5e922fc0
--- /dev/null
+++ b/agent/src/test/java/com/code_intelligence/jazzer/runtime/FuzzedDataProviderImplTest.java
@@ -0,0 +1,225 @@
+// Copyright 2021 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.code_intelligence.jazzer.runtime;
+
+import com.code_intelligence.jazzer.api.FuzzedDataProvider;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+public class FuzzedDataProviderImplTest {
+ public static void main(String[] args) {
+ try (FuzzedDataProviderImpl fuzzedDataProvider =
+ FuzzedDataProviderImpl.withJavaData(INPUT_BYTES)) {
+ verifyFuzzedDataProvider(fuzzedDataProvider);
+ }
+ }
+
+ private strictfp static void verifyFuzzedDataProvider(FuzzedDataProvider data) {
+ assertEqual(true, data.consumeBoolean());
+
+ assertEqual((byte) 0x7F, data.consumeByte());
+ assertEqual((byte) 0x14, data.consumeByte((byte) 0x12, (byte) 0x22));
+
+ assertEqual(0x12345678, data.consumeInt());
+ assertEqual(-0x12345600, data.consumeInt(-0x12345678, -0x12345600));
+ assertEqual(0x12345679, data.consumeInt(0x12345678, 0x12345679));
+
+ assertEqual(true, Arrays.equals(new byte[] {0x01, 0x02}, data.consumeBytes(2)));
+
+ assertEqual("jazzer", data.consumeString(6));
+ assertEqual("ja\u0000zer", data.consumeString(6));
+ assertEqual("ۧ", data.consumeString(2));
+
+ assertEqual("jazzer", data.consumeAsciiString(6));
+ assertEqual("ja\u0000zer", data.consumeAsciiString(6));
+ assertEqual("\u0062\u0002\u002C\u0043\u001F", data.consumeAsciiString(5));
+
+ assertEqual(true,
+ Arrays.equals(new boolean[] {false, false, true, false, true}, data.consumeBooleans(5)));
+ assertEqual(true,
+ Arrays.equals(new long[] {0x0123456789abdcefL, 0xfedcba9876543210L}, data.consumeLongs(2)));
+
+ assertEqual((float) 0.28969181, data.consumeProbabilityFloat());
+ assertEqual(0.086814121166605432, data.consumeProbabilityDouble());
+ assertEqual((float) 0.30104411, data.consumeProbabilityFloat());
+ assertEqual(0.96218831486039413, data.consumeProbabilityDouble());
+
+ assertEqual((float) -2.8546307e+38, data.consumeRegularFloat());
+ assertEqual(8.0940194040236032e+307, data.consumeRegularDouble());
+ assertEqual((float) 271.49084, data.consumeRegularFloat((float) 123.0, (float) 777.0));
+ assertEqual(30.859126145478349, data.consumeRegularDouble(13.37, 31.337));
+
+ assertEqual((float) 0.0, data.consumeFloat());
+ assertEqual((float) -0.0, data.consumeFloat());
+ assertEqual(Float.POSITIVE_INFINITY, data.consumeFloat());
+ assertEqual(Float.NEGATIVE_INFINITY, data.consumeFloat());
+ assertEqual(true, Float.isNaN(data.consumeFloat()));
+ assertEqual(Float.MIN_VALUE, data.consumeFloat());
+ assertEqual(-Float.MIN_VALUE, data.consumeFloat());
+ assertEqual(Float.MIN_NORMAL, data.consumeFloat());
+ assertEqual(-Float.MIN_NORMAL, data.consumeFloat());
+ assertEqual(Float.MAX_VALUE, data.consumeFloat());
+ assertEqual(-Float.MAX_VALUE, data.consumeFloat());
+
+ assertEqual(0.0, data.consumeDouble());
+ assertEqual(-0.0, data.consumeDouble());
+ assertEqual(Double.POSITIVE_INFINITY, data.consumeDouble());
+ assertEqual(Double.NEGATIVE_INFINITY, data.consumeDouble());
+ assertEqual(true, Double.isNaN(data.consumeDouble()));
+ assertEqual(Double.MIN_VALUE, data.consumeDouble());
+ assertEqual(-Double.MIN_VALUE, data.consumeDouble());
+ assertEqual(Double.MIN_NORMAL, data.consumeDouble());
+ assertEqual(-Double.MIN_NORMAL, data.consumeDouble());
+ assertEqual(Double.MAX_VALUE, data.consumeDouble());
+ assertEqual(-Double.MAX_VALUE, data.consumeDouble());
+
+ int[] array = {0, 1, 2, 3, 4};
+ assertEqual(4, data.pickValue(array));
+ assertEqual(2, (int) data.pickValue(Arrays.stream(array).boxed().toArray()));
+ assertEqual(3, data.pickValue(Arrays.stream(array).boxed().collect(Collectors.toList())));
+ assertEqual(2, data.pickValue(Arrays.stream(array).boxed().collect(Collectors.toSet())));
+
+ // Buffer is almost depleted at this point.
+ assertEqual(7, data.remainingBytes());
+ assertEqual(true, Arrays.equals(new long[0], data.consumeLongs(3)));
+ assertEqual(7, data.remainingBytes());
+ assertEqual(true, Arrays.equals(new int[] {0x12345678}, data.consumeInts(3)));
+ assertEqual(3, data.remainingBytes());
+ assertEqual(0x123456L, data.consumeLong());
+
+ // Buffer has been fully consumed at this point
+ assertEqual(0, data.remainingBytes());
+ assertEqual(0, data.consumeInt());
+ assertEqual(0.0, data.consumeDouble());
+ assertEqual(-13.37, data.consumeRegularDouble(-13.37, 31.337));
+ assertEqual(true, Arrays.equals(new byte[0], data.consumeBytes(4)));
+ assertEqual(true, Arrays.equals(new long[0], data.consumeLongs(4)));
+ assertEqual("", data.consumeRemainingAsAsciiString());
+ assertEqual("", data.consumeRemainingAsString());
+ assertEqual("", data.consumeAsciiString(100));
+ assertEqual("", data.consumeString(100));
+ }
+
+ private static <T extends Comparable<T>> void assertEqual(T a, T b) {
+ if (a.compareTo(b) != 0) {
+ throw new IllegalArgumentException("Expected: " + a + ", got: " + b);
+ }
+ }
+
+ private static final byte[] INPUT_BYTES = new byte[] {
+ // Bytes read from the start
+ 0x01, 0x02, // consumeBytes(2): {0x01, 0x02}
+
+ 'j', 'a', 'z', 'z', 'e', 'r', // consumeString(6): "jazzer"
+ 'j', 'a', 0x00, 'z', 'e', 'r', // consumeString(6): "ja\u0000zer"
+ (byte) 0xE2, (byte) 0x82, (byte) 0xAC, (byte) 0xC3, (byte) 0x9F, // consumeString(2): "€ẞ"
+
+ 'j', 'a', 'z', 'z', 'e', 'r', // consumeAsciiString(6): "jazzer"
+ 'j', 'a', 0x00, 'z', 'e', 'r', // consumeAsciiString(6): "ja\u0000zer"
+ (byte) 0xE2, (byte) 0x82, (byte) 0xAC, (byte) 0xC3,
+ (byte) 0x9F, // consumeAsciiString(5): "\u0062\u0002\u002C\u0043\u001F"
+
+ 0, 0, 1, 0, 1, // consumeBooleans(5): { false, false, true, false, true }
+ (byte) 0xEF, (byte) 0xDC, (byte) 0xAB, (byte) 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54,
+ 0x76, (byte) 0x98, (byte) 0xBA, (byte) 0xDC, (byte) 0xFE,
+ // consumeLongs(2): { 0x0123456789ABCDEF, 0xFEDCBA9876543210 }
+
+ 0x78, 0x56, 0x34, 0x12, // consumeInts(3): { 0x12345678 }
+ 0x56, 0x34, 0x12, // consumeLong():
+
+ // Bytes read from the end
+ 0x02, 0x03, 0x02, 0x04, // 4x pickValue in array with five elements
+
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56,
+ 0x78, // consumed but unused by consumeDouble()
+ 10, // -max for next consumeDouble
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56,
+ 0x78, // consumed but unused by consumeDouble()
+ 9, // max for next consumeDouble
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56,
+ 0x78, // consumed but unused by consumeDouble()
+ 8, // -min for next consumeDouble
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56,
+ 0x78, // consumed but unused by consumeDouble()
+ 7, // min for next consumeDouble
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56,
+ 0x78, // consumed but unused by consumeDouble()
+ 6, // -denorm_min for next consumeDouble
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56,
+ 0x78, // consumed but unused by consumeDouble()
+ 5, // denorm_min for next consumeDouble
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56,
+ 0x78, // consumed but unused by consumeDouble()
+ 4, // NaN for next consumeDouble
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56,
+ 0x78, // consumed but unused by consumeDouble()
+ 3, // -infinity for next consumeDouble
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56,
+ 0x78, // consumed but unused by consumeDouble()
+ 2, // infinity for next consumeDouble
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56,
+ 0x78, // consumed but unused by consumeDouble()
+ 1, // -0.0 for next consumeDouble
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, 0x12, 0x34, 0x56,
+ 0x78, // consumed but unused by consumeDouble()
+ 0, // 0.0 for next consumeDouble
+
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat()
+ 10, // -max for next consumeFloat
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat()
+ 9, // max for next consumeFloat
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat()
+ 8, // -min for next consumeFloat
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat()
+ 7, // min for next consumeFloat
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat()
+ 6, // -denorm_min for next consumeFloat
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat()
+ 5, // denorm_min for next consumeFloat
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat()
+ 4, // NaN for next consumeFloat
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat()
+ 3, // -infinity for next consumeFloat
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat()
+ 2, // infinity for next consumeFloat
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat()
+ 1, // -0.0 for next consumeFloat
+ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, // consumed but unused by consumeFloat()
+ 0, // 0.0 for next consumeFloat
+
+ (byte) 0x88, (byte) 0xAB, 0x61, (byte) 0xCB, 0x32, (byte) 0xEB, 0x30, (byte) 0xF9,
+ // consumeDouble(13.37, 31.337): 30.859126145478349 (small range)
+ 0x51, (byte) 0xF6, 0x1F, 0x3A, // consumeFloat(123.0, 777.0): 271.49084 (small range)
+ 0x11, 0x4D, (byte) 0xFD, 0x54, (byte) 0xD6, 0x3D, 0x43, 0x73, 0x39,
+ // consumeRegularDouble(): 8.0940194040236032e+307
+ 0x16, (byte) 0xCF, 0x3D, 0x29, 0x4A, // consumeRegularFloat(): -2.8546307e+38
+
+ 0x61, (byte) 0xCB, 0x32, (byte) 0xEB, 0x30, (byte) 0xF9, 0x51, (byte) 0xF6,
+ // consumeProbabilityDouble(): 0.96218831486039413
+ 0x1F, 0x3A, 0x11, 0x4D, // consumeProbabilityFloat(): 0.30104411
+ (byte) 0xFD, 0x54, (byte) 0xD6, 0x3D, 0x43, 0x73, 0x39, 0x16,
+ // consumeProbabilityDouble(): 0.086814121166605432
+ (byte) 0xCF, 0x3D, 0x29, 0x4A, // consumeProbabilityFloat(): 0.28969181
+
+ 0x01, // consumeInt(0x12345678, 0x12345679): 0x12345679
+ 0x78, // consumeInt(-0x12345678, -0x12345600): -0x12345600
+ 0x78, 0x56, 0x34, 0x12, // consumeInt(): 0x12345678
+
+ 0x02, // consumeByte(0x12, 0x22): 0x14
+ 0x7F, // consumeByte(): 0x7F
+
+ 0x01, // consumeBool(): true
+ };
+}
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/runtime/RecordingFuzzedDataProviderTest.java b/agent/src/test/java/com/code_intelligence/jazzer/runtime/RecordingFuzzedDataProviderTest.java
new file mode 100644
index 00000000..d58a5ca9
--- /dev/null
+++ b/agent/src/test/java/com/code_intelligence/jazzer/runtime/RecordingFuzzedDataProviderTest.java
@@ -0,0 +1,214 @@
+// Copyright 2021 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.code_intelligence.jazzer.runtime;
+
+import com.code_intelligence.jazzer.api.CannedFuzzedDataProvider;
+import com.code_intelligence.jazzer.api.FuzzedDataProvider;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class RecordingFuzzedDataProviderTest {
+ @Test
+ public void testRecordingFuzzedDataProvider() throws IOException {
+ FuzzedDataProvider mockData = new MockFuzzedDataProvider();
+ String referenceResult = sampleFuzzTarget(mockData);
+
+ FuzzedDataProvider recordingMockData =
+ RecordingFuzzedDataProvider.makeFuzzedDataProviderProxy(mockData);
+ Assert.assertEquals(referenceResult, sampleFuzzTarget(recordingMockData));
+
+ String cannedMockDataString =
+ RecordingFuzzedDataProvider.serializeFuzzedDataProviderProxy(recordingMockData);
+ FuzzedDataProvider cannedMockData = new CannedFuzzedDataProvider(cannedMockDataString);
+ Assert.assertEquals(referenceResult, sampleFuzzTarget(cannedMockData));
+ }
+
+ private String sampleFuzzTarget(FuzzedDataProvider data) {
+ StringBuilder result = new StringBuilder();
+ result.append(data.consumeString(10));
+ int[] ints = data.consumeInts(5);
+ result.append(Arrays.stream(ints).mapToObj(Integer::toString).collect(Collectors.joining(",")));
+ result.append(data.pickValue(ints));
+ result.append(data.consumeString(20));
+ result.append(data.pickValues(Arrays.stream(ints).boxed().collect(Collectors.toSet()), 5)
+ .stream()
+ .map(Integer::toHexString)
+ .collect(Collectors.joining(",")));
+ result.append(data.remainingBytes());
+ return result.toString();
+ }
+
+ private static final class MockFuzzedDataProvider implements FuzzedDataProvider {
+ @Override
+ public boolean consumeBoolean() {
+ return true;
+ }
+
+ @Override
+ public boolean[] consumeBooleans(int maxLength) {
+ return new boolean[] {false, true};
+ }
+
+ @Override
+ public byte consumeByte() {
+ return 2;
+ }
+
+ @Override
+ public byte consumeByte(byte min, byte max) {
+ return max;
+ }
+
+ @Override
+ public short consumeShort() {
+ return 2;
+ }
+
+ @Override
+ public short consumeShort(short min, short max) {
+ return min;
+ }
+
+ @Override
+ public short[] consumeShorts(int maxLength) {
+ return new short[] {2, 4, 7};
+ }
+
+ @Override
+ public int consumeInt() {
+ return 5;
+ }
+
+ @Override
+ public int consumeInt(int min, int max) {
+ return max;
+ }
+
+ @Override
+ public int[] consumeInts(int maxLength) {
+ return IntStream.range(0, maxLength).toArray();
+ }
+
+ @Override
+ public long consumeLong() {
+ return 42;
+ }
+
+ @Override
+ public long consumeLong(long min, long max) {
+ return min;
+ }
+
+ @Override
+ public long[] consumeLongs(int maxLength) {
+ return LongStream.range(0, maxLength).toArray();
+ }
+
+ @Override
+ public float consumeFloat() {
+ return Float.NaN;
+ }
+
+ @Override
+ public float consumeRegularFloat() {
+ return 0.3f;
+ }
+
+ @Override
+ public float consumeRegularFloat(float min, float max) {
+ return min;
+ }
+
+ @Override
+ public float consumeProbabilityFloat() {
+ return 0.2f;
+ }
+
+ @Override
+ public double consumeDouble() {
+ return Double.NaN;
+ }
+
+ @Override
+ public double consumeRegularDouble(double min, double max) {
+ return max;
+ }
+
+ @Override
+ public double consumeRegularDouble() {
+ return Math.PI;
+ }
+
+ @Override
+ public double consumeProbabilityDouble() {
+ return 0.5;
+ }
+
+ @Override
+ public char consumeChar() {
+ return 'C';
+ }
+
+ @Override
+ public char consumeChar(char min, char max) {
+ return min;
+ }
+
+ @Override
+ public char consumeCharNoSurrogates() {
+ return 'C';
+ }
+
+ @Override
+ public String consumeAsciiString(int maxLength) {
+ return "foobar";
+ }
+
+ @Override
+ public String consumeString(int maxLength) {
+ return "fooۊ";
+ }
+
+ @Override
+ public String consumeRemainingAsAsciiString() {
+ return "foobar";
+ }
+
+ @Override
+ public String consumeRemainingAsString() {
+ return "foobar";
+ }
+
+ @Override
+ public byte[] consumeBytes(int maxLength) {
+ return new byte[maxLength];
+ }
+
+ @Override
+ public byte[] consumeRemainingAsBytes() {
+ return new byte[] {1};
+ }
+
+ @Override
+ public int remainingBytes() {
+ return 1;
+ }
+ }
+}
diff --git a/agent/src/test/java/com/code_intelligence/jazzer/runtime/TraceCmpHooksTest.java b/agent/src/test/java/com/code_intelligence/jazzer/runtime/TraceCmpHooksTest.java
new file mode 100644
index 00000000..9275ca30
--- /dev/null
+++ b/agent/src/test/java/com/code_intelligence/jazzer/runtime/TraceCmpHooksTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 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.code_intelligence.jazzer.runtime;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TraceCmpHooksTest {
+ private static final ExecutorService ES = Executors.newFixedThreadPool(5);
+
+ @Test
+ public void cmpHookShouldHandleConcurrentModifications() throws InterruptedException {
+ String arg = "test";
+ Map<String, Object> map = new HashMap<>();
+ map.put(arg, arg);
+
+ // Add elements to map asynchronously
+ Function<Integer, Runnable> put = (final Integer num) -> () -> {
+ map.put(String.valueOf(num), num);
+ };
+ for (int i = 0; i < 1_000_000; i++) {
+ ES.submit(put.apply(i));
+ }
+
+ // Call hook
+ for (int i = 0; i < 1_000; i++) {
+ TraceCmpHooks.mapGet(null, map, new Object[] {arg}, 1, null);
+ }
+
+ ES.shutdown();
+ // noinspection ResultOfMethodCallIgnored
+ ES.awaitTermination(5, TimeUnit.SECONDS);
+ }
+}