aboutsummaryrefslogtreecommitdiff
path: root/agent
diff options
context:
space:
mode:
authorFabian Meumertzheim <fabian@meumertzhe.im>2021-10-14 18:31:30 +0200
committerFabian Meumertzheim <fabian@meumertzhe.im>2021-10-19 11:07:51 +0200
commit7f7f6b1d40fa80e9d32da27a3366735e2c38ca38 (patch)
treede460c080a5499caf3bb93da2003515f6d2f86f2 /agent
parentc6908c85cb42f12767434873e5b94e90003c6bc0 (diff)
downloadjazzer-api-7f7f6b1d40fa80e9d32da27a3366735e2c38ca38.tar.gz
Implement the autofuzz fuzz target
Diffstat (limited to 'agent')
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzConstructionException.java25
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java28
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzInvocationException.java25
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java100
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java65
5 files changed, 220 insertions, 23 deletions
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzConstructionException.java b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzConstructionException.java
new file mode 100644
index 00000000..760fef3d
--- /dev/null
+++ b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzConstructionException.java
@@ -0,0 +1,25 @@
+// 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.autofuzz;
+
+/**
+ * An exception wrapping a {@link Throwable} thrown during the construction of parameters for, but
+ * not the actual invocation of an autofuzzed method.
+ */
+public class AutofuzzConstructionException extends RuntimeException {
+ public AutofuzzConstructionException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java
new file mode 100644
index 00000000..65abc492
--- /dev/null
+++ b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java
@@ -0,0 +1,28 @@
+// 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.autofuzz;
+
+/**
+ * An error indicating an internal error in the autofuzz functionality.
+ */
+public class AutofuzzError extends Error {
+ public AutofuzzError(String message) {
+ super(message);
+ }
+
+ public AutofuzzError(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzInvocationException.java b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzInvocationException.java
new file mode 100644
index 00000000..8e586c4f
--- /dev/null
+++ b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzInvocationException.java
@@ -0,0 +1,25 @@
+// 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.autofuzz;
+
+/**
+ * An exception wrapping a {@link Throwable} thrown during the actual invocation of, but not the
+ * construction of parameters for an autofuzzed method.
+ */
+public class AutofuzzInvocationException extends RuntimeException {
+ public AutofuzzInvocationException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java
new file mode 100644
index 00000000..15cab149
--- /dev/null
+++ b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java
@@ -0,0 +1,100 @@
+// 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.autofuzz;
+
+import com.code_intelligence.jazzer.api.FuzzedDataProvider;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class FuzzTarget {
+ private static final long MAX_EXECUTIONS_WITHOUT_INVOCATION = 100;
+
+ private static String methodReference;
+ private static Method[] targetMethods;
+ private static Map<Method, Class<?>[]> throwsDeclarations;
+ private static long executionsSinceLastInvocation = 0;
+
+ public static void fuzzerInitialize(String[] args) {
+ if (args.length != 1 || !args[0].contains("::")) {
+ System.err.println(
+ "Expected the argument to --autofuzz to be a method reference (e.g. System.out::println");
+ System.exit(1);
+ }
+ methodReference = args[0];
+ String[] parts = methodReference.split("::", 2);
+ String className = parts[0];
+ String methodName = parts[1];
+
+ Class<?> targetClass;
+ try {
+ targetClass = Thread.currentThread().getContextClassLoader().loadClass(className);
+ } catch (ClassNotFoundException e) {
+ System.err.printf(
+ "Failed to find class %s for autofuzz, please ensure it is contained in the classpath "
+ + "specified with --cp and specify the full package name%n",
+ className);
+ e.printStackTrace();
+ System.exit(1);
+ return;
+ }
+
+ targetMethods = Arrays.stream(targetClass.getMethods())
+ .filter(method -> method.getName().equals(methodName))
+ .toArray(Method[] ::new);
+ if (targetMethods.length == 0) {
+ System.err.printf("Failed to find accessible methods named %s in class %s for autofuzz",
+ methodName, className);
+ }
+ throwsDeclarations =
+ Arrays.stream(targetMethods)
+ .collect(Collectors.toMap(method -> method, Method::getExceptionTypes));
+ }
+
+ public static void fuzzerTestOneInput(FuzzedDataProvider data) throws Throwable {
+ Method targetMethod = data.pickValue(targetMethods);
+ try {
+ Meta.autofuzz(data, targetMethod);
+ executionsSinceLastInvocation = 0;
+ } catch (AutofuzzConstructionException ignored) {
+ // Ignore exceptions thrown while constructing the parameters for the target method. We can
+ // only guess how to generate valid parameters and any exceptions thrown while doing so
+ // are most likely on us. However, if this happens too often, Autofuzz got stuck and we should
+ // let the user know.
+ executionsSinceLastInvocation++;
+ if (executionsSinceLastInvocation >= MAX_EXECUTIONS_WITHOUT_INVOCATION) {
+ System.err.printf("Failed to generate valid arguments to '%s' in %d attempts; giving up%n",
+ methodReference, executionsSinceLastInvocation);
+ System.exit(1);
+ }
+ } catch (AutofuzzInvocationException e) {
+ executionsSinceLastInvocation = 0;
+ Throwable cause = e.getCause();
+ Class<?> causeClass = cause.getClass();
+ // Do not report exceptions declared to be thrown by the method under test.
+ for (Class<?> declaredThrow : throwsDeclarations.get(targetMethod)) {
+ if (declaredThrow.isAssignableFrom(causeClass)) {
+ return;
+ }
+ }
+ throw cause;
+ } catch (Throwable t) {
+ System.err.println("Unexpected exception encountered during autofuzz");
+ t.printStackTrace();
+ System.exit(1);
+ }
+ }
+}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java
index 8a640637..8f229d51 100644
--- a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java
+++ b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java
@@ -96,12 +96,23 @@ public class Meta {
private static Object consumeChecked(FuzzedDataProvider data, Class<?>[] types, int i) {
if (types[i] == Unknown.class) {
- throw new IllegalArgumentException("Failed to determine type of argument " + (i + 1));
+ throw new AutofuzzError("Failed to determine type of argument " + (i + 1));
+ }
+ Object result;
+ try {
+ result = consume(data, types[i]);
+ } catch (AutofuzzConstructionException e) {
+ // Do not nest AutofuzzConstructionExceptions.
+ throw e;
+ } catch (AutofuzzInvocationException e) {
+ // If an invocation fails while creating the arguments for another invocation, the exception
+ // should not be reported, so we rewrap it.
+ throw new AutofuzzConstructionException(e.getCause());
+ } catch (Throwable t) {
+ throw new AutofuzzConstructionException(t);
}
- Object result = consume(data, types[i]);
if (result != null && !types[i].isAssignableFrom(result.getClass())) {
- throw new IllegalStateException(
- "consume returned " + result.getClass() + ", but need " + types[i]);
+ throw new AutofuzzError("consume returned " + result.getClass() + ", but need " + types[i]);
}
return result;
}
@@ -118,10 +129,11 @@ public class Meta {
Object[] arguments = consumeArguments(data, method);
try {
return method.invoke(thisObject, arguments);
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
+ } catch (IllegalAccessException | IllegalArgumentException | NullPointerException e) {
+ // We should ensure that the arguments fed into the method are always valid.
+ throw new AutofuzzError(e);
} catch (InvocationTargetException e) {
- throw new RuntimeException(e.getCause());
+ throw new AutofuzzInvocationException(e.getCause());
}
}
@@ -129,30 +141,37 @@ public class Meta {
Object[] arguments = consumeArguments(data, constructor);
try {
return constructor.newInstance(arguments);
- } catch (InstantiationException | IllegalAccessException e) {
- throw new RuntimeException(e);
+ } catch (InstantiationException | IllegalAccessException | IllegalArgumentException e) {
+ // This should never be reached as the logic in consume should prevent us from e.g. calling
+ // constructors of abstract classes or private constructors.
+ throw new AutofuzzError(e);
} catch (InvocationTargetException e) {
- throw new RuntimeException(e.getCause());
+ throw new AutofuzzInvocationException(e.getCause());
}
}
private static Object[] consumeArguments(FuzzedDataProvider data, Executable executable) {
- return Arrays.stream(executable.getParameterTypes())
- .map((type) -> consume(data, type))
- .toArray();
+ Object[] result;
+ try {
+ result = Arrays.stream(executable.getParameterTypes())
+ .map((type) -> consume(data, type))
+ .toArray();
+ return result;
+ } catch (AutofuzzConstructionException e) {
+ // Do not nest AutofuzzConstructionExceptions.
+ throw e;
+ } catch (AutofuzzInvocationException e) {
+ // If an invocation fails while creating the arguments for another invocation, the exception
+ // should not be reported, so we rewrap it.
+ throw new AutofuzzConstructionException(e.getCause());
+ } catch (Throwable t) {
+ throw new AutofuzzConstructionException(t);
+ }
}
public static <T1> void autofuzz(FuzzedDataProvider data, Consumer1<T1> func) {
- Class<?> type = TypeResolver.resolveRawArgument(Consumer1.class, func.getClass());
- if (type == Unknown.class) {
- throw new IllegalArgumentException("Failed to determine type of argument 1");
- }
- Object result = consume(data, type);
- if (result != null && !type.isAssignableFrom(result.getClass())) {
- throw new IllegalStateException(
- "consume returned " + result.getClass() + ", but need " + type);
- }
- func.accept((T1) result);
+ Class<?>[] types = TypeResolver.resolveRawArguments(Consumer1.class, func.getClass());
+ func.accept((T1) consumeChecked(data, types, 0));
}
public static <T1, R> R autofuzz(FuzzedDataProvider data, Function1<T1, R> func) {