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