diff options
author | Fabian Meumertzheim <meumertzheim@code-intelligence.com> | 2021-10-18 15:54:42 +0200 |
---|---|---|
committer | Fabian Meumertzheim <fabian@meumertzhe.im> | 2021-10-19 11:07:51 +0200 |
commit | eff82062143cdcc13f3a20d1fec944dc4de9187e (patch) | |
tree | ce1d003faff27468671c630ceb3b30c697cfd94e /agent/src/main/java | |
parent | 11ad0812c16539a4f19836cc7ad242240247c220 (diff) | |
download | jazzer-api-eff82062143cdcc13f3a20d1fec944dc4de9187e.tar.gz |
Add autofuzz debug mode
This mode prints detailed information in failure cases and can be
enabled by setting the JAZZER_AUTOFUZZ_DEBUG env variable to a non-empty
value.
Diffstat (limited to 'agent/src/main/java')
4 files changed, 60 insertions, 22 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 index e1d775c4..7cb41d4b 100644 --- a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzConstructionException.java +++ b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzConstructionException.java @@ -19,6 +19,9 @@ package com.code_intelligence.jazzer.autofuzz; * not the actual invocation of an autofuzzed method. */ public class AutofuzzConstructionException extends RuntimeException { + public AutofuzzConstructionException() { + super(); + } public AutofuzzConstructionException(String message) { super(message); } 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 index 65abc492..2773deea 100644 --- a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java +++ b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java @@ -22,6 +22,10 @@ public class AutofuzzError extends Error { super(message); } + public AutofuzzError(String message, Throwable cause) { + super(message, cause); + } + public AutofuzzError(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 index 9ad44420..9806bb3c 100644 --- a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java +++ b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java @@ -83,7 +83,7 @@ public class FuzzTarget { .toArray(Method[] ::new); if (targetMethods.length == 0) { if (descriptor == null) { - System.err.printf("%nFailed to find accessible methods named %s in class %s for autofuzz.%n" + System.err.printf("Failed to find accessible methods named %s in class %s for autofuzz.%n" + "Accessible methods:%n%s", methodName, className, Arrays.stream(targetClass.getMethods()) @@ -93,7 +93,7 @@ public class FuzzTarget { .distinct() .collect(Collectors.joining(System.lineSeparator()))); } else { - System.err.printf("%nFailed to find accessible methods named %s in class %s for autofuzz.%n" + System.err.printf("Failed to find accessible methods named %s in class %s for autofuzz.%n" + "Accessible methods with that name:%n%s", methodName, className, Arrays.stream(targetClass.getMethods()) @@ -121,7 +121,10 @@ public class FuzzTarget { try { Meta.autofuzz(data, targetMethod); executionsSinceLastInvocation = 0; - } catch (AutofuzzConstructionException ignored) { + } catch (AutofuzzConstructionException e) { + if (Meta.isDebug()) { + e.printStackTrace(); + } // 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 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 94d1e921..dfa6c4a3 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 @@ -15,6 +15,7 @@ package com.code_intelligence.jazzer.autofuzz; import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.utils.Utils; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfoList; import io.github.classgraph.ScanResult; @@ -50,7 +51,7 @@ public class Meta { return method.invoke(thisObject, arguments); } catch (IllegalAccessException | IllegalArgumentException | NullPointerException e) { // We should ensure that the arguments fed into the method are always valid. - throw new AutofuzzError(e); + throw new AutofuzzError(getDebugSummary(method, thisObject, arguments), e); } catch (InvocationTargetException e) { throw new AutofuzzInvocationException(e.getCause()); } @@ -63,7 +64,7 @@ public class Meta { } 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); + throw new AutofuzzError(getDebugSummary(constructor, null, arguments), e); } catch (InvocationTargetException e) { throw new AutofuzzInvocationException(e.getCause()); } @@ -149,8 +150,12 @@ public class Meta { } } if (implementingClasses.isEmpty()) { - throw new AutofuzzConstructionException(String.format( - "Could not find classes implementing %s on the classpath", type.getName())); + if (isDebug()) { + throw new AutofuzzConstructionException(String.format( + "Could not find classes implementing %s on the classpath", type.getName())); + } else { + throw new AutofuzzConstructionException(); + } } return consume(data, data.pickValue(implementingClasses)); } else if (type.getConstructors().length > 0) { @@ -168,10 +173,11 @@ public class Meta { } return obj; } - // We are out of more or less canonical ways to construct an instance of this class and have to resort to more - // heuristic approaches. + // We are out of more or less canonical ways to construct an instance of this class and have to + // resort to more heuristic approaches. - // First, try to find nested classes with names ending in Builder and call a subset of their chaining methods. + // First, try to find nested classes with names ending in Builder and call a subset of their + // chaining methods. List<Class<?>> nestedBuilderClasses = getNestedBuilderClasses(type); if (!nestedBuilderClasses.isEmpty()) { Class<?> pickedBuilder = data.pickValue(nestedBuilderClasses); @@ -197,24 +203,46 @@ public class Meta { } catch (Exception e) { throw new AutofuzzConstructionException(e); } + } + + // We ran out of ways to construct an instance of the requested type. If in debug mode, report + // more detailed information. + if (!isDebug()) { + throw new AutofuzzConstructionException(); } else { - Constructor<?>[] c = type.getDeclaredConstructors(); - System.err.printf("ctor: %s\n", c[0].toGenericString()); - System.err.printf(" public %b\n", Modifier.isPublic(c[0].getModifiers())); - System.err.printf(" private %b\n", Modifier.isPrivate(c[0].getModifiers())); - System.err.printf(" protected %b\n", Modifier.isProtected(c[0].getModifiers())); - // System.err.printf(" protected %b\n", Modifier.i(c[0].getModifiers())); + String summary = String.format( + "Failed to generate instance of %s:%nAccessible constructors: %s%nNested subclasses: %s%n", + type.getName(), + Arrays.stream(type.getConstructors()) + .map(Utils::getReadableDescriptor) + .collect(Collectors.joining(", ")), + Arrays.stream(type.getClasses()).map(Class::getName).collect(Collectors.joining(", "))); + throw new AutofuzzConstructionException(summary); } - return null; + } + + static boolean isDebug() { + String value = System.getenv("JAZZER_AUTOFUZZ_DEBUG"); + return value != null && !value.isEmpty(); + } + + private static String getDebugSummary( + Executable executable, Object thisObject, Object[] arguments) { + return String.format("%nMethod: %s::%s%s%nthis: %s%nArguments: %s", + executable.getDeclaringClass().getName(), executable.getName(), + Utils.getReadableDescriptor(executable), thisObject, + Arrays.stream(arguments) + .map(arg -> arg == null ? "null" : arg.toString()) + .collect(Collectors.joining(", "))); } private static List<Class<?>> getNestedBuilderClasses(Class<?> type) { List<Class<?>> nestedBuilderClasses = nestedBuilderClassesCache.get(type); if (nestedBuilderClasses == null) { nestedBuilderClasses = Arrays.stream(type.getClasses()) - .filter(cls -> cls.getName().endsWith("Builder")) - .filter(cls -> !getOriginalObjectCreationMethods(cls).isEmpty()) - .collect(Collectors.toList()); + .filter(cls -> cls.getName().endsWith("Builder")) + .filter(cls -> !getOriginalObjectCreationMethods(cls).isEmpty()) + .collect(Collectors.toList()); nestedBuilderClassesCache.put(type, nestedBuilderClasses); } return nestedBuilderClasses; @@ -223,13 +251,13 @@ public class Meta { private static List<Method> getOriginalObjectCreationMethods(Class<?> builder) { List<Method> originalObjectCreationMethods = originalObjectCreationMethodsCache.get(builder); if (originalObjectCreationMethods == null) { - originalObjectCreationMethods = Arrays.stream(builder.getMethods()) + originalObjectCreationMethods = + Arrays.stream(builder.getMethods()) .filter(m -> m.getReturnType() == builder.getEnclosingClass()) .collect(Collectors.toList()); originalObjectCreationMethodsCache.put(builder, originalObjectCreationMethods); } return originalObjectCreationMethods; - } private static List<Method> getPotentialSetters(Class<?> type) { |