From 3c45ad069681214810206e3a5c4522000aa46b30 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Tue, 19 Oct 2021 12:02:06 +0200 Subject: Sort the return values of reflection methods We sort by name (and descriptor, if necessary) to ensure that the order and thus our picks are a deterministic function of the fuzzer input across all JVMs. --- .../code_intelligence/jazzer/autofuzz/Meta.java | 49 ++++++++++++++++------ 1 file changed, 37 insertions(+), 12 deletions(-) (limited to 'agent') 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 943b9acd..f151dea0 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 @@ -37,6 +37,7 @@ public class Meta { static WeakHashMap, List>> nestedBuilderClassesCache = new WeakHashMap<>(); static WeakHashMap, List> originalObjectCreationMethodsCache = new WeakHashMap<>(); + static WeakHashMap, List> cascadingBuilderMethodsCache = new WeakHashMap<>(); public static Object autofuzz(FuzzedDataProvider data, Method method) { if (Modifier.isStatic(method.getModifiers())) { @@ -145,9 +146,9 @@ public class Meta { } else if (type == Class.class) { return YourAverageJavaClass.class; } else if (type == Method.class) { - return data.pickValue(YourAverageJavaClass.class.getMethods()); + return data.pickValue(sortExecutables(YourAverageJavaClass.class.getMethods())); } else if (type == Constructor.class) { - return data.pickValue(YourAverageJavaClass.class.getConstructors()); + return data.pickValue(sortExecutables(YourAverageJavaClass.class.getConstructors())); } else if (type.isInterface() || Modifier.isAbstract(type.getModifiers())) { List> implementingClasses = implementingClassesCache.get(type); if (implementingClasses == null) { @@ -175,7 +176,7 @@ public class Meta { } return consume(data, data.pickValue(implementingClasses)); } else if (type.getConstructors().length > 0) { - Constructor constructor = data.pickValue(type.getConstructors()); + Constructor constructor = data.pickValue(sortExecutables(type.getConstructors())); Object obj = autofuzz(data, constructor); if (constructor.getParameterCount() == 0) { List potentialSetters = getPotentialSetters(type); @@ -197,19 +198,15 @@ public class Meta { List> nestedBuilderClasses = getNestedBuilderClasses(type); if (!nestedBuilderClasses.isEmpty()) { Class pickedBuilder = data.pickValue(nestedBuilderClasses); - - List cascadingBuilderMethods = Arrays.stream(pickedBuilder.getMethods()) - .filter(m -> m.getReturnType() == pickedBuilder) - .collect(Collectors.toList()); - + List cascadingBuilderMethods = getCascadingBuilderMethods(pickedBuilder); List originalObjectCreationMethods = getOriginalObjectCreationMethods(pickedBuilder); int pickedMethodsNumber = data.consumeInt(0, cascadingBuilderMethods.size()); List pickedMethods = data.pickValues(cascadingBuilderMethods, pickedMethodsNumber); - Method builderMethod = data.pickValue(originalObjectCreationMethods); - Object builderObj = autofuzz(data, data.pickValue(pickedBuilder.getConstructors())); + Object builderObj = + autofuzz(data, data.pickValue(sortExecutables(pickedBuilder.getConstructors()))); for (Method method : pickedMethods) { builderObj = autofuzz(data, method, builderObj); } @@ -257,6 +254,20 @@ public class Meta { .collect(Collectors.joining(", "))); } + private static List sortExecutables(T[] executables) { + List list = Arrays.asList(executables); + sortExecutables(list); + return list; + } + + private static void sortExecutables(List executables) { + executables.sort(Comparator.comparing(Executable::getName).thenComparing(Utils::getDescriptor)); + } + + private static void sortClasses(List> classes) { + classes.sort(Comparator.comparing(Class::getName)); + } + private static List> getNestedBuilderClasses(Class type) { List> nestedBuilderClasses = nestedBuilderClassesCache.get(type); if (nestedBuilderClasses == null) { @@ -264,6 +275,7 @@ public class Meta { .filter(cls -> cls.getName().endsWith("Builder")) .filter(cls -> !getOriginalObjectCreationMethods(cls).isEmpty()) .collect(Collectors.toList()); + sortClasses(nestedBuilderClasses); nestedBuilderClassesCache.put(type, nestedBuilderClasses); } return nestedBuilderClasses; @@ -276,21 +288,34 @@ public class Meta { Arrays.stream(builder.getMethods()) .filter(m -> m.getReturnType() == builder.getEnclosingClass()) .collect(Collectors.toList()); + sortExecutables(originalObjectCreationMethods); originalObjectCreationMethodsCache.put(builder, originalObjectCreationMethods); } return originalObjectCreationMethods; } + private static List getCascadingBuilderMethods(Class builder) { + List cascadingBuilderMethods = cascadingBuilderMethodsCache.get(builder); + if (cascadingBuilderMethods == null) { + cascadingBuilderMethods = Arrays.stream(builder.getMethods()) + .filter(m -> m.getReturnType() == builder) + .collect(Collectors.toList()); + sortExecutables(cascadingBuilderMethods); + cascadingBuilderMethodsCache.put(builder, cascadingBuilderMethods); + } + return cascadingBuilderMethods; + } + private static List getPotentialSetters(Class type) { List potentialSetters = new ArrayList<>(); - List methods = Arrays.asList(type.getMethods()); - methods.sort(Comparator.comparing(Method::getName)); + Method[] methods = type.getMethods(); for (Method method : methods) { if (void.class.equals(method.getReturnType()) && method.getParameterCount() == 1 && method.getName().startsWith("set")) { potentialSetters.add(method); } } + sortExecutables(potentialSetters); return potentialSetters; } -- cgit v1.2.3