diff options
author | Fabian Meumertzheim <fabian@meumertzhe.im> | 2021-10-14 18:31:30 +0200 |
---|---|---|
committer | Fabian Meumertzheim <fabian@meumertzhe.im> | 2021-10-19 11:07:51 +0200 |
commit | 7f7f6b1d40fa80e9d32da27a3366735e2c38ca38 (patch) | |
tree | de460c080a5499caf3bb93da2003515f6d2f86f2 /agent/src | |
parent | c6908c85cb42f12767434873e5b94e90003c6bc0 (diff) | |
download | jazzer-api-7f7f6b1d40fa80e9d32da27a3366735e2c38ca38.tar.gz |
Implement the autofuzz fuzz target
Diffstat (limited to 'agent/src')
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) { |