xref: /aosp_15_r20/external/jazzer-api/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java (revision 33edd6723662ea34453766bfdca85dbfdd5342b8)
1 // Copyright 2021 Code Intelligence GmbH
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.code_intelligence.jazzer.autofuzz;
16 
17 import com.code_intelligence.jazzer.api.AutofuzzConstructionException;
18 import com.code_intelligence.jazzer.api.AutofuzzInvocationException;
19 import com.code_intelligence.jazzer.api.Consumer1;
20 import com.code_intelligence.jazzer.api.Consumer2;
21 import com.code_intelligence.jazzer.api.Consumer3;
22 import com.code_intelligence.jazzer.api.Consumer4;
23 import com.code_intelligence.jazzer.api.Consumer5;
24 import com.code_intelligence.jazzer.api.Function1;
25 import com.code_intelligence.jazzer.api.Function2;
26 import com.code_intelligence.jazzer.api.Function3;
27 import com.code_intelligence.jazzer.api.Function4;
28 import com.code_intelligence.jazzer.api.Function5;
29 import com.code_intelligence.jazzer.api.FuzzedDataProvider;
30 import com.code_intelligence.jazzer.runtime.HardToCatchError;
31 import com.code_intelligence.jazzer.utils.Utils;
32 import io.github.classgraph.ClassGraph;
33 import io.github.classgraph.ClassInfoList;
34 import io.github.classgraph.ScanResult;
35 import java.io.ByteArrayInputStream;
36 import java.io.InputStream;
37 import java.lang.reflect.Array;
38 import java.lang.reflect.Constructor;
39 import java.lang.reflect.Executable;
40 import java.lang.reflect.GenericArrayType;
41 import java.lang.reflect.InvocationTargetException;
42 import java.lang.reflect.Method;
43 import java.lang.reflect.Modifier;
44 import java.lang.reflect.ParameterizedType;
45 import java.lang.reflect.Type;
46 import java.lang.reflect.TypeVariable;
47 import java.lang.reflect.WildcardType;
48 import java.util.Arrays;
49 import java.util.HashMap;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.WeakHashMap;
53 import java.util.stream.Collectors;
54 import java.util.stream.IntStream;
55 import net.jodah.typetools.TypeResolver;
56 import net.jodah.typetools.TypeResolver.Unknown;
57 
58 public class Meta {
59   public static final boolean IS_DEBUG = isDebug();
60 
61   private static final Meta PUBLIC_LOOKUP_INSTANCE = new Meta(null);
62   private static final boolean IS_TEST = isTest();
63   private static final WeakHashMap<Class<?>, List<Class<?>>> implementingClassesCache =
64       new WeakHashMap<>();
65   private static final WeakHashMap<Class<?>, List<Class<?>>> nestedBuilderClassesCache =
66       new WeakHashMap<>();
67   private static final WeakHashMap<Class<?>, List<Method>> originalObjectCreationMethodsCache =
68       new WeakHashMap<>();
69   private static final WeakHashMap<Class<?>, List<Method>> cascadingBuilderMethodsCache =
70       new WeakHashMap<>();
71 
72   private final AccessibleObjectLookup lookup;
73 
Meta(Class<?> referenceClass)74   public Meta(Class<?> referenceClass) {
75     lookup = new AccessibleObjectLookup(referenceClass);
76   }
77 
78   @SuppressWarnings("unchecked")
autofuzz(FuzzedDataProvider data, Consumer1<T1> func)79   public static <T1> void autofuzz(FuzzedDataProvider data, Consumer1<T1> func) {
80     Class<?>[] types = TypeResolver.resolveRawArguments(Consumer1.class, func.getClass());
81     func.accept((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0));
82   }
83 
84   @SuppressWarnings("unchecked")
autofuzz(FuzzedDataProvider data, Consumer2<T1, T2> func)85   public static <T1, T2> void autofuzz(FuzzedDataProvider data, Consumer2<T1, T2> func) {
86     Class<?>[] types = TypeResolver.resolveRawArguments(Consumer2.class, func.getClass());
87     func.accept((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0),
88         (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1));
89   }
90 
91   @SuppressWarnings("unchecked")
autofuzz(FuzzedDataProvider data, Consumer3<T1, T2, T3> func)92   public static <T1, T2, T3> void autofuzz(FuzzedDataProvider data, Consumer3<T1, T2, T3> func) {
93     Class<?>[] types = TypeResolver.resolveRawArguments(Consumer3.class, func.getClass());
94     func.accept((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0),
95         (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1),
96         (T3) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 2));
97   }
98 
99   @SuppressWarnings("unchecked")
autofuzz( FuzzedDataProvider data, Consumer4<T1, T2, T3, T4> func)100   public static <T1, T2, T3, T4> void autofuzz(
101       FuzzedDataProvider data, Consumer4<T1, T2, T3, T4> func) {
102     Class<?>[] types = TypeResolver.resolveRawArguments(Consumer4.class, func.getClass());
103     func.accept((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0),
104         (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1),
105         (T3) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 2),
106         (T4) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 3));
107   }
108 
109   @SuppressWarnings("unchecked")
autofuzz( FuzzedDataProvider data, Consumer5<T1, T2, T3, T4, T5> func)110   public static <T1, T2, T3, T4, T5> void autofuzz(
111       FuzzedDataProvider data, Consumer5<T1, T2, T3, T4, T5> func) {
112     Class<?>[] types = TypeResolver.resolveRawArguments(Consumer5.class, func.getClass());
113     func.accept((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0),
114         (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1),
115         (T3) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 2),
116         (T4) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 3),
117         (T5) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 4));
118   }
119 
120   @SuppressWarnings("unchecked")
autofuzz(FuzzedDataProvider data, Function1<T1, R> func)121   public static <T1, R> R autofuzz(FuzzedDataProvider data, Function1<T1, R> func) {
122     Class<?>[] types = TypeResolver.resolveRawArguments(Function1.class, func.getClass());
123     return func.apply((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0));
124   }
125 
126   @SuppressWarnings("unchecked")
autofuzz(FuzzedDataProvider data, Function2<T1, T2, R> func)127   public static <T1, T2, R> R autofuzz(FuzzedDataProvider data, Function2<T1, T2, R> func) {
128     Class<?>[] types = TypeResolver.resolveRawArguments(Function2.class, func.getClass());
129     return func.apply((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0),
130         (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1));
131   }
132 
133   @SuppressWarnings("unchecked")
autofuzz(FuzzedDataProvider data, Function3<T1, T2, T3, R> func)134   public static <T1, T2, T3, R> R autofuzz(FuzzedDataProvider data, Function3<T1, T2, T3, R> func) {
135     Class<?>[] types = TypeResolver.resolveRawArguments(Function3.class, func.getClass());
136     return func.apply((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0),
137         (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1),
138         (T3) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 2));
139   }
140 
141   @SuppressWarnings("unchecked")
autofuzz( FuzzedDataProvider data, Function4<T1, T2, T3, T4, R> func)142   public static <T1, T2, T3, T4, R> R autofuzz(
143       FuzzedDataProvider data, Function4<T1, T2, T3, T4, R> func) {
144     Class<?>[] types = TypeResolver.resolveRawArguments(Function4.class, func.getClass());
145     return func.apply((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0),
146         (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1),
147         (T3) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 2),
148         (T4) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 3));
149   }
150 
151   @SuppressWarnings("unchecked")
autofuzz( FuzzedDataProvider data, Function5<T1, T2, T3, T4, T5, R> func)152   public static <T1, T2, T3, T4, T5, R> R autofuzz(
153       FuzzedDataProvider data, Function5<T1, T2, T3, T4, T5, R> func) {
154     Class<?>[] types = TypeResolver.resolveRawArguments(Function5.class, func.getClass());
155     return func.apply((T1) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 0),
156         (T2) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 1),
157         (T3) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 2),
158         (T4) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 3),
159         (T5) PUBLIC_LOOKUP_INSTANCE.consumeChecked(data, types, 4));
160   }
161 
consume(FuzzedDataProvider data, Class<?> type)162   public static Object consume(FuzzedDataProvider data, Class<?> type) {
163     return PUBLIC_LOOKUP_INSTANCE.consume(data, type, null);
164   }
165 
rescanClasspath()166   static void rescanClasspath() {
167     implementingClassesCache.clear();
168   }
169 
isTest()170   private static boolean isTest() {
171     String value = System.getenv("JAZZER_AUTOFUZZ_TESTING");
172     return value != null && !value.isEmpty();
173   }
174 
isDebug()175   private static boolean isDebug() {
176     String value = System.getenv("JAZZER_AUTOFUZZ_DEBUG");
177     return value != null && !value.isEmpty();
178   }
179 
consumeArrayLength(FuzzedDataProvider data, int sizeOfElement)180   private static int consumeArrayLength(FuzzedDataProvider data, int sizeOfElement) {
181     // Spend at most half of the fuzzer input bytes so that the remaining arguments that require
182     // construction still have non-trivial data to work with.
183     int bytesToSpend = data.remainingBytes() / 2;
184     return bytesToSpend / Math.max(sizeOfElement, 1);
185   }
186 
deepToString(Object obj)187   private static String deepToString(Object obj) {
188     if (obj == null) {
189       return "null";
190     }
191     if (obj.getClass().isArray()) {
192       return String.format("(%s[]) %s", obj.getClass().getComponentType().getName(),
193           Arrays.deepToString((Object[]) obj));
194     }
195     return obj.toString();
196   }
197 
getDebugSummary( Executable executable, Object thisObject, Object[] arguments)198   private static String getDebugSummary(
199       Executable executable, Object thisObject, Object[] arguments) {
200     return String.format("%nMethod: %s::%s%s%nthis: %s%nArguments: %s",
201         executable.getDeclaringClass().getName(), executable.getName(),
202         Utils.getReadableDescriptor(executable), thisObject,
203         Arrays.stream(arguments).map(Meta::deepToString).collect(Collectors.joining(", ")));
204   }
205 
getRawType(Type genericType)206   static Class<?> getRawType(Type genericType) {
207     if (genericType instanceof Class<?>) {
208       return (Class<?>) genericType;
209     } else if (genericType instanceof ParameterizedType) {
210       return getRawType(((ParameterizedType) genericType).getRawType());
211     } else if (genericType instanceof WildcardType) {
212       // TODO: Improve this.
213       return Object.class;
214     } else if (genericType instanceof TypeVariable<?>) {
215       throw new AutofuzzError("Did not expect genericType to be a TypeVariable: " + genericType);
216     } else if (genericType instanceof GenericArrayType) {
217       return Array
218           .newInstance(getRawType(((GenericArrayType) genericType).getGenericComponentType()), 0)
219           .getClass();
220     } else {
221       throw new AutofuzzError("Got unexpected class implementing Type: " + genericType);
222     }
223   }
224 
autofuzz(FuzzedDataProvider data, Method method)225   public Object autofuzz(FuzzedDataProvider data, Method method) {
226     return autofuzz(data, method, null);
227   }
228 
229   // Renamed so that it doesn't clash with the static method consume, which we don't want to rename
230   // as the api package depends on it by name.
consumeNonStatic(FuzzedDataProvider data, Class<?> type)231   public Object consumeNonStatic(FuzzedDataProvider data, Class<?> type) {
232     return consume(data, type, null);
233   }
234 
autofuzz(FuzzedDataProvider data, Method method, AutofuzzCodegenVisitor visitor)235   Object autofuzz(FuzzedDataProvider data, Method method, AutofuzzCodegenVisitor visitor) {
236     Object result;
237     if (Modifier.isStatic(method.getModifiers())) {
238       if (visitor != null) {
239         // This group will always have two elements: The class name and the method call.
240         visitor.pushGroup(
241             String.format("%s.", method.getDeclaringClass().getCanonicalName()), "", "");
242       }
243       try {
244         result = autofuzz(data, method, null, visitor);
245       } finally {
246         if (visitor != null) {
247           visitor.popGroup();
248         }
249       }
250     } else {
251       if (visitor != null) {
252         // This group will always have two elements: The thisObject and the method call.
253         // Since the this object can be a complex expression, wrap it in parenthesis.
254         visitor.pushGroup("(", ").", "");
255       }
256       try {
257         Object thisObject = consume(data, method.getDeclaringClass(), visitor);
258         if (thisObject == null) {
259           throw new AutofuzzConstructionException();
260         }
261         result = autofuzz(data, method, thisObject, visitor);
262       } finally {
263         if (visitor != null) {
264           visitor.popGroup();
265         }
266       }
267     }
268     return result;
269   }
270 
autofuzz(FuzzedDataProvider data, Method method, Object thisObject)271   public Object autofuzz(FuzzedDataProvider data, Method method, Object thisObject) {
272     return autofuzz(data, method, thisObject, null);
273   }
274 
autofuzz( FuzzedDataProvider data, Method method, Object thisObject, AutofuzzCodegenVisitor visitor)275   Object autofuzz(
276       FuzzedDataProvider data, Method method, Object thisObject, AutofuzzCodegenVisitor visitor) {
277     if (visitor != null) {
278       visitor.pushGroup(String.format("%s(", method.getName()), ", ", ")");
279     }
280     Object[] arguments = consumeArguments(data, method, visitor);
281     if (visitor != null) {
282       visitor.popGroup();
283     }
284     try {
285       return method.invoke(thisObject, arguments);
286     } catch (IllegalAccessException | IllegalArgumentException | NullPointerException e) {
287       // We should ensure that the arguments fed into the method are always valid.
288       throw new AutofuzzError(getDebugSummary(method, thisObject, arguments), e);
289     } catch (InvocationTargetException e) {
290       if (e.getCause() instanceof HardToCatchError) {
291         throw new AutofuzzInvocationException();
292       }
293       throw new AutofuzzInvocationException(e.getCause());
294     }
295   }
296 
autofuzzForConsume( FuzzedDataProvider data, Constructor<?> constructor, AutofuzzCodegenVisitor visitor)297   Object autofuzzForConsume(
298       FuzzedDataProvider data, Constructor<?> constructor, AutofuzzCodegenVisitor visitor) {
299     try {
300       return autofuzz(data, constructor, visitor);
301     } catch (AutofuzzConstructionException e) {
302       // Do not nest AutofuzzConstructionExceptions.
303       throw e;
304     } catch (AutofuzzInvocationException e) {
305       // If an invocation fails during consume and thus while trying to construct a valid object,
306       // the exception should not be reported as a finding, so we rewrap it.
307       throw new AutofuzzConstructionException(e.getCause());
308     } catch (Throwable t) {
309       throw new AutofuzzConstructionException(t);
310     }
311   }
312 
autofuzzForConsume( FuzzedDataProvider data, Method method, Object thisObject, AutofuzzCodegenVisitor visitor)313   Object autofuzzForConsume(
314       FuzzedDataProvider data, Method method, Object thisObject, AutofuzzCodegenVisitor visitor) {
315     try {
316       return autofuzz(data, method, thisObject, visitor);
317     } catch (AutofuzzConstructionException e) {
318       // Do not nest AutofuzzConstructionExceptions.
319       throw e;
320     } catch (AutofuzzInvocationException e) {
321       // If an invocation fails during consume and thus while trying to construct a valid object,
322       // the exception should not be reported as a finding, so we rewrap it.
323       throw new AutofuzzConstructionException(e.getCause());
324     } catch (Throwable t) {
325       throw new AutofuzzConstructionException(t);
326     }
327   }
328 
autofuzz(FuzzedDataProvider data, Constructor<R> constructor)329   public <R> R autofuzz(FuzzedDataProvider data, Constructor<R> constructor) {
330     return autofuzz(data, constructor, null);
331   }
332 
autofuzz( FuzzedDataProvider data, Constructor<R> constructor, AutofuzzCodegenVisitor visitor)333   <R> R autofuzz(
334       FuzzedDataProvider data, Constructor<R> constructor, AutofuzzCodegenVisitor visitor) {
335     if (visitor != null) {
336       // getCanonicalName is correct also for nested classes.
337       visitor.pushGroup(
338           String.format("new %s(", constructor.getDeclaringClass().getCanonicalName()), ", ", ")");
339     }
340     Object[] arguments = consumeArguments(data, constructor, visitor);
341     if (visitor != null) {
342       visitor.popGroup();
343     }
344     try {
345       return constructor.newInstance(arguments);
346     } catch (InstantiationException | IllegalAccessException | IllegalArgumentException e) {
347       // This should never be reached as the logic in consume should prevent us from e.g. calling
348       // constructors of abstract classes or private constructors.
349       throw new AutofuzzError(getDebugSummary(constructor, null, arguments), e);
350     } catch (InvocationTargetException e) {
351       if (e.getCause() instanceof HardToCatchError) {
352         throw new AutofuzzInvocationException();
353       }
354       throw new AutofuzzInvocationException(e.getCause());
355     }
356   }
357 
358   // Invariant: The Java source code representation of the returned object visited by visitor must
359   // represent an object of the same type as genericType. For example, a null value returned for
360   // the genericType Class<java.lang.String> should lead to the generated code
361   // "(java.lang.String) null", not just "null". This makes it possible to safely use consume in
362   // recursive argument constructions.
363   // Exception: Some Java libraries offer public methods that take private interfaces or abstract
364   // classes as parameters. In this case, a cast to the parent type would cause an
365   // IllegalAccessError. Since this case should be rare and there is no good alternative to
366   // disambiguate overloads, we omit the cast in this case.
consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor visitor)367   Object consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor visitor) {
368     Class<?> type = getRawType(genericType);
369     if (type == byte.class || type == Byte.class) {
370       byte result = data.consumeByte();
371       if (visitor != null) {
372         visitor.pushElement(String.format("(byte) %s", result));
373       }
374       return result;
375     } else if (type == short.class || type == Short.class) {
376       short result = data.consumeShort();
377       if (visitor != null) {
378         visitor.pushElement(String.format("(short) %s", result));
379       }
380       return result;
381     } else if (type == int.class || type == Integer.class) {
382       int result = data.consumeInt();
383       if (visitor != null) {
384         visitor.pushElement(Integer.toString(result));
385       }
386       return result;
387     } else if (type == long.class || type == Long.class) {
388       long result = data.consumeLong();
389       if (visitor != null) {
390         visitor.pushElement(String.format("%sL", result));
391       }
392       return result;
393     } else if (type == float.class || type == Float.class) {
394       float result = data.consumeFloat();
395       if (visitor != null) {
396         visitor.pushElement(String.format("%sF", result));
397       }
398       return result;
399     } else if (type == double.class || type == Double.class) {
400       double result = data.consumeDouble();
401       if (visitor != null) {
402         visitor.pushElement(Double.toString(result));
403       }
404       return result;
405     } else if (type == boolean.class || type == Boolean.class) {
406       boolean result = data.consumeBoolean();
407       if (visitor != null) {
408         visitor.pushElement(Boolean.toString(result));
409       }
410       return result;
411     } else if (type == char.class || type == Character.class) {
412       char result = data.consumeChar();
413       if (visitor != null) {
414         visitor.addCharLiteral(result);
415       }
416       return result;
417     }
418     // Sometimes, but rarely return null for non-primitive and non-boxed types.
419     // TODO: We might want to return null for boxed types sometimes, but this is complicated by the
420     //       fact that TypeUtils can't distinguish between a primitive type and its wrapper and may
421     //       thus easily cause false-positive NullPointerExceptions.
422     if (!type.isPrimitive() && data.consumeByte() == 0) {
423       if (visitor != null) {
424         if (type == Object.class) {
425           visitor.pushElement("null");
426         } else {
427           visitor.pushElement(String.format("(%s) null", type.getCanonicalName()));
428         }
429       }
430       return null;
431     }
432     if (type == String.class || type == CharSequence.class) {
433       String result = data.consumeString(consumeArrayLength(data, 1));
434       if (visitor != null) {
435         visitor.addStringLiteral(result);
436       }
437       return result;
438     } else if (type.isArray()) {
439       if (type == byte[].class) {
440         byte[] result = data.consumeBytes(consumeArrayLength(data, Byte.BYTES));
441         if (visitor != null) {
442           visitor.pushElement(IntStream.range(0, result.length)
443                                   .mapToObj(i -> "(byte) " + result[i])
444                                   .collect(Collectors.joining(", ", "new byte[]{", "}")));
445         }
446         return result;
447       } else if (type == int[].class) {
448         int[] result = data.consumeInts(consumeArrayLength(data, Integer.BYTES));
449         if (visitor != null) {
450           visitor.pushElement(Arrays.stream(result)
451                                   .mapToObj(String::valueOf)
452                                   .collect(Collectors.joining(", ", "new int[]{", "}")));
453         }
454         return result;
455       } else if (type == short[].class) {
456         short[] result = data.consumeShorts(consumeArrayLength(data, Short.BYTES));
457         if (visitor != null) {
458           visitor.pushElement(IntStream.range(0, result.length)
459                                   .mapToObj(i -> "(short) " + result[i])
460                                   .collect(Collectors.joining(", ", "new short[]{", "}")));
461         }
462         return result;
463       } else if (type == long[].class) {
464         long[] result = data.consumeLongs(consumeArrayLength(data, Long.BYTES));
465         if (visitor != null) {
466           visitor.pushElement(Arrays.stream(result)
467                                   .mapToObj(e -> e + "L")
468                                   .collect(Collectors.joining(", ", "new long[]{", "}")));
469         }
470         return result;
471       } else if (type == boolean[].class) {
472         boolean[] result = data.consumeBooleans(consumeArrayLength(data, 1));
473         if (visitor != null) {
474           visitor.pushElement(
475               Arrays.toString(result).replace(']', '}').replace("[", "new boolean[]{"));
476         }
477         return result;
478       } else {
479         if (visitor != null) {
480           visitor.pushGroup(
481               String.format("new %s[]{", type.getComponentType().getName()), ", ", "}");
482         }
483         int remainingBytesBeforeFirstElementCreation = data.remainingBytes();
484         Object firstElement = consume(data, type.getComponentType(), visitor);
485         int remainingBytesAfterFirstElementCreation = data.remainingBytes();
486         int sizeOfElementEstimate =
487             remainingBytesBeforeFirstElementCreation - remainingBytesAfterFirstElementCreation;
488         Object array = Array.newInstance(
489             type.getComponentType(), consumeArrayLength(data, sizeOfElementEstimate));
490         for (int i = 0; i < Array.getLength(array); i++) {
491           if (i == 0) {
492             Array.set(array, i, firstElement);
493           } else {
494             Array.set(array, i, consume(data, type.getComponentType(), visitor));
495           }
496         }
497         if (visitor != null) {
498           if (Array.getLength(array) == 0) {
499             // We implicitly pushed the first element with the call to consume above, but it is not
500             // part of the array.
501             visitor.popElement();
502           }
503           visitor.popGroup();
504         }
505         return array;
506       }
507     } else if (type == ByteArrayInputStream.class || type == InputStream.class) {
508       byte[] array = data.consumeBytes(consumeArrayLength(data, Byte.BYTES));
509       if (visitor != null) {
510         visitor.pushElement(IntStream.range(0, array.length)
511                                 .mapToObj(i -> "(byte) " + array[i])
512                                 .collect(Collectors.joining(
513                                     ", ", "new java.io.ByteArrayInputStream(new byte[]{", "})")));
514       }
515       return new ByteArrayInputStream(array);
516     } else if (type == Map.class) {
517       ParameterizedType mapType = (ParameterizedType) genericType;
518       if (mapType.getActualTypeArguments().length != 2) {
519         throw new AutofuzzError(
520             "Expected Map generic type to have two type parameters: " + mapType);
521       }
522       Type keyType = mapType.getActualTypeArguments()[0];
523       Type valueType = mapType.getActualTypeArguments()[1];
524       if (visitor != null) {
525         // Do not use Collectors.toMap() since it cannot handle null values.
526         // Also annotate the type of the entry stream since it might be empty, in which case type
527         // inference on the accumulator could fail.
528         visitor.pushGroup(
529             String.format("java.util.stream.Stream.<java.util.AbstractMap.SimpleEntry<%s, %s>>of(",
530                 keyType.getTypeName(), valueType.getTypeName()),
531             ", ",
532             ").collect(java.util.HashMap::new, (map, e) -> map.put(e.getKey(), e.getValue()), java.util.HashMap::putAll)");
533       }
534       int remainingBytesBeforeFirstEntryCreation = data.remainingBytes();
535       if (visitor != null) {
536         visitor.pushGroup("new java.util.AbstractMap.SimpleEntry<>(", ", ", ")");
537       }
538       Object firstKey = consume(data, keyType, visitor);
539       Object firstValue = consume(data, valueType, visitor);
540       if (visitor != null) {
541         visitor.popGroup();
542       }
543       int remainingBytesAfterFirstEntryCreation = data.remainingBytes();
544       int sizeOfElementEstimate =
545           remainingBytesBeforeFirstEntryCreation - remainingBytesAfterFirstEntryCreation;
546       int mapSize = consumeArrayLength(data, sizeOfElementEstimate);
547       Map<Object, Object> map = new HashMap<>(mapSize);
548       for (int i = 0; i < mapSize; i++) {
549         if (i == 0) {
550           map.put(firstKey, firstValue);
551         } else {
552           if (visitor != null) {
553             visitor.pushGroup("new java.util.AbstractMap.SimpleEntry<>(", ", ", ")");
554           }
555           map.put(consume(data, keyType, visitor), consume(data, valueType, visitor));
556           if (visitor != null) {
557             visitor.popGroup();
558           }
559         }
560       }
561       if (visitor != null) {
562         if (mapSize == 0) {
563           // We implicitly pushed the first entry with the call to consume above, but it is not
564           // part of the array.
565           visitor.popElement();
566         }
567         visitor.popGroup();
568       }
569       return map;
570     } else if (type.isEnum()) {
571       Enum<?> enumValue = (Enum<?>) data.pickValue(type.getEnumConstants());
572       if (visitor != null) {
573         visitor.pushElement(String.format("%s.%s", type.getName(), enumValue.name()));
574       }
575       return enumValue;
576     } else if (type == Class.class) {
577       if (visitor != null) {
578         visitor.pushElement(String.format("%s.class", YourAverageJavaClass.class.getName()));
579       }
580       return YourAverageJavaClass.class;
581     } else if (type == Method.class) {
582       if (visitor != null) {
583         throw new AutofuzzError("codegen has not been implemented for Method.class");
584       }
585       return data.pickValue(lookup.getAccessibleMethods(YourAverageJavaClass.class));
586     } else if (type == Constructor.class) {
587       if (visitor != null) {
588         throw new AutofuzzError("codegen has not been implemented for Constructor.class");
589       }
590       return data.pickValue(lookup.getAccessibleConstructors(YourAverageJavaClass.class));
591     } else if (type.isInterface() || Modifier.isAbstract(type.getModifiers())) {
592       List<Class<?>> implementingClasses = implementingClassesCache.get(type);
593       if (implementingClasses == null) {
594         // TODO: We may be scanning multiple times. Instead, we should keep the ScanResult around
595         //  for as long as there is enough memory.
596         ClassGraph classGraph = new ClassGraph()
597                                     .enableClassInfo()
598                                     .ignoreClassVisibility()
599                                     .ignoreMethodVisibility()
600                                     .enableInterClassDependencies()
601                                     .rejectPackages("jaz");
602         if (!IS_TEST) {
603           classGraph.rejectPackages("com.code_intelligence.jazzer");
604         }
605         try (ScanResult result = classGraph.scan()) {
606           ClassInfoList children =
607               type.isInterface() ? result.getClassesImplementing(type) : result.getSubclasses(type);
608           implementingClasses = children.getStandardClasses()
609                                     .filter(info -> !Modifier.isAbstract(info.getModifiers()))
610                                     .filter(info -> lookup.isAccessible(info, info.getModifiers()))
611                                     // Filter out anonymous and local classes, which can't be
612                                     // instantiated in reproducers.
613                                     .filter(info -> info.getName() != null)
614                                     .loadClasses();
615           implementingClassesCache.put(type, implementingClasses);
616         }
617       }
618       if (implementingClasses.isEmpty()) {
619         if (IS_DEBUG) {
620           throw new AutofuzzConstructionException(String.format(
621               "Could not find classes implementing %s on the classpath", type.getName()));
622         } else {
623           throw new AutofuzzConstructionException();
624         }
625       }
626       if (visitor != null) {
627         // See the "Exception" note in the method comment.
628         if (Modifier.isPublic(type.getModifiers())) {
629           // This group will always have a single element: The instance of the implementing class.
630           visitor.pushGroup(String.format("(%s) ", type.getCanonicalName()), "", "");
631         }
632       }
633       Object result = consume(data, data.pickValue(implementingClasses), visitor);
634       if (visitor != null) {
635         if (Modifier.isPublic(type.getModifiers())) {
636           visitor.popGroup();
637         }
638       }
639       return result;
640     }
641     Constructor<?>[] constructors = lookup.getAccessibleConstructors(type);
642     if (constructors.length > 0) {
643       Constructor<?> constructor = data.pickValue(constructors);
644       boolean applySetters = constructor.getParameterCount() == 0;
645       if (visitor != null && applySetters) {
646         // Embed the instance creation and setters into an immediately invoked lambda expression to
647         // turn them into an expression.
648         String uniqueVariableName = visitor.uniqueVariableName();
649         visitor.pushGroup(String.format("((java.util.function.Supplier<%1$s>) (() -> {%1$s %2$s = ",
650                               type.getCanonicalName(), uniqueVariableName),
651             String.format("; %s.", uniqueVariableName),
652             String.format("; return %s;})).get()", uniqueVariableName));
653       }
654       Object obj = autofuzzForConsume(data, constructor, visitor);
655       if (applySetters) {
656         List<Method> potentialSetters = getPotentialSetters(type);
657         if (!potentialSetters.isEmpty()) {
658           List<Method> pickedSetters =
659               data.pickValues(potentialSetters, data.consumeInt(0, potentialSetters.size()));
660           for (Method setter : pickedSetters) {
661             autofuzzForConsume(data, setter, obj, visitor);
662           }
663         }
664         if (visitor != null) {
665           visitor.popGroup();
666         }
667       }
668       return obj;
669     }
670     // We are out of more or less canonical ways to construct an instance of this class and have to
671     // resort to more heuristic approaches.
672 
673     // First, try to find nested classes with names ending in Builder and call a subset of their
674     // chaining methods.
675     List<Class<?>> nestedBuilderClasses = getNestedBuilderClasses(type);
676     if (!nestedBuilderClasses.isEmpty()) {
677       Class<?> pickedBuilder = data.pickValue(nestedBuilderClasses);
678       List<Method> cascadingBuilderMethods = getCascadingBuilderMethods(pickedBuilder);
679       List<Method> originalObjectCreationMethods = getOriginalObjectCreationMethods(pickedBuilder);
680 
681       int pickedMethodsNumber = data.consumeInt(0, cascadingBuilderMethods.size());
682       List<Method> pickedMethods = data.pickValues(cascadingBuilderMethods, pickedMethodsNumber);
683       Method builderMethod = data.pickValue(originalObjectCreationMethods);
684 
685       if (visitor != null) {
686         // Group for the chain of builder methods.
687         visitor.pushGroup("", ".", "");
688       }
689       Object builderObj = autofuzzForConsume(
690           data, data.pickValue(lookup.getAccessibleConstructors(pickedBuilder)), visitor);
691       for (Method method : pickedMethods) {
692         builderObj = autofuzzForConsume(data, method, builderObj, visitor);
693       }
694 
695       try {
696         Object obj = autofuzzForConsume(data, builderMethod, builderObj, visitor);
697         if (visitor != null) {
698           visitor.popGroup();
699         }
700         return obj;
701       } catch (Exception e) {
702         throw new AutofuzzConstructionException(e);
703       }
704     }
705 
706     // We ran out of ways to construct an instance of the requested type. If in debug mode, report
707     // more detailed information.
708     if (IS_DEBUG) {
709       String summary = String.format(
710           "Failed to generate instance of %s:%nAccessible constructors: %s%nNested subclasses: %s%n",
711           type.getName(),
712           Arrays.stream(lookup.getAccessibleConstructors(type))
713               .map(Utils::getReadableDescriptor)
714               .collect(Collectors.joining(", ")),
715           Arrays.stream(lookup.getAccessibleClasses(type))
716               .map(Class::getName)
717               .collect(Collectors.joining(", ")));
718       throw new AutofuzzConstructionException(summary);
719     } else {
720       throw new AutofuzzConstructionException();
721     }
722   }
723 
getNestedBuilderClasses(Class<?> type)724   private List<Class<?>> getNestedBuilderClasses(Class<?> type) {
725     List<Class<?>> nestedBuilderClasses = nestedBuilderClassesCache.get(type);
726     if (nestedBuilderClasses == null) {
727       nestedBuilderClasses = Arrays.stream(lookup.getAccessibleClasses(type))
728                                  .filter(cls -> cls.getName().endsWith("Builder"))
729                                  .filter(cls -> !getOriginalObjectCreationMethods(cls).isEmpty())
730                                  .collect(Collectors.toList());
731       nestedBuilderClassesCache.put(type, nestedBuilderClasses);
732     }
733     return nestedBuilderClasses;
734   }
735 
getOriginalObjectCreationMethods(Class<?> builder)736   private List<Method> getOriginalObjectCreationMethods(Class<?> builder) {
737     List<Method> originalObjectCreationMethods = originalObjectCreationMethodsCache.get(builder);
738     if (originalObjectCreationMethods == null) {
739       originalObjectCreationMethods =
740           Arrays.stream(lookup.getAccessibleMethods(builder))
741               .filter(m -> m.getReturnType() == builder.getEnclosingClass())
742               .collect(Collectors.toList());
743       originalObjectCreationMethodsCache.put(builder, originalObjectCreationMethods);
744     }
745     return originalObjectCreationMethods;
746   }
747 
getCascadingBuilderMethods(Class<?> builder)748   private List<Method> getCascadingBuilderMethods(Class<?> builder) {
749     List<Method> cascadingBuilderMethods = cascadingBuilderMethodsCache.get(builder);
750     if (cascadingBuilderMethods == null) {
751       cascadingBuilderMethods = Arrays.stream(lookup.getAccessibleMethods(builder))
752                                     .filter(m -> m.getReturnType() == builder)
753                                     .collect(Collectors.toList());
754       cascadingBuilderMethodsCache.put(builder, cascadingBuilderMethods);
755     }
756     return cascadingBuilderMethods;
757   }
758 
getPotentialSetters(Class<?> type)759   private List<Method> getPotentialSetters(Class<?> type) {
760     return Arrays.stream(lookup.getAccessibleMethods(type))
761         .filter(method -> void.class.equals(method.getReturnType()))
762         .filter(method -> method.getParameterCount() == 1)
763         .filter(method -> method.getName().startsWith("set"))
764         .collect(Collectors.toList());
765   }
766 
consumeArguments( FuzzedDataProvider data, Executable executable, AutofuzzCodegenVisitor visitor)767   public Object[] consumeArguments(
768       FuzzedDataProvider data, Executable executable, AutofuzzCodegenVisitor visitor) {
769     Object[] result;
770     try {
771       result = Arrays.stream(executable.getGenericParameterTypes())
772                    .map(type -> consume(data, type, visitor))
773                    .toArray();
774       return result;
775     } catch (AutofuzzConstructionException e) {
776       // Do not nest AutofuzzConstructionExceptions.
777       throw e;
778     } catch (AutofuzzInvocationException e) {
779       // If an invocation fails while creating the arguments for another invocation, the exception
780       // should not be reported, so we rewrap it.
781       throw new AutofuzzConstructionException(e.getCause());
782     } catch (Throwable t) {
783       throw new AutofuzzConstructionException(t);
784     }
785   }
786 
consumeChecked(FuzzedDataProvider data, Class<?>[] types, int i)787   private Object consumeChecked(FuzzedDataProvider data, Class<?>[] types, int i) {
788     if (types[i] == Unknown.class) {
789       throw new AutofuzzError("Failed to determine type of argument " + (i + 1));
790     }
791     Object result;
792     try {
793       result = consumeNonStatic(data, types[i]);
794     } catch (AutofuzzConstructionException e) {
795       // Do not nest AutofuzzConstructionExceptions.
796       throw e;
797     } catch (AutofuzzInvocationException e) {
798       // If an invocation fails while creating the arguments for another invocation, the exception
799       // should not be reported, so we rewrap it.
800       throw new AutofuzzConstructionException(e.getCause());
801     } catch (Throwable t) {
802       throw new AutofuzzConstructionException(t);
803     }
804     if (result != null && !types[i].isAssignableFrom(result.getClass())) {
805       throw new AutofuzzError("consume returned " + result.getClass() + ", but need " + types[i]);
806     }
807     return result;
808   }
809 }
810