1 /* 2 * Copyright 2022 Code Intelligence GmbH 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.code_intelligence.jazzer.driver; 18 19 import static com.code_intelligence.jazzer.runtime.Constants.IS_ANDROID; 20 import static java.lang.System.exit; 21 22 import com.code_intelligence.jazzer.api.FuzzedDataProvider; 23 import com.code_intelligence.jazzer.driver.FuzzTargetHolder.FuzzTarget; 24 import com.code_intelligence.jazzer.utils.Log; 25 import com.code_intelligence.jazzer.utils.ManifestUtils; 26 import java.lang.reflect.Method; 27 import java.lang.reflect.Modifier; 28 import java.util.Arrays; 29 import java.util.List; 30 import java.util.Optional; 31 import java.util.concurrent.Callable; 32 import java.util.stream.Collectors; 33 import java.util.stream.Stream; 34 35 class FuzzTargetFinder { 36 private static final String FUZZER_TEST_ONE_INPUT = "fuzzerTestOneInput"; 37 private static final String FUZZER_INITIALIZE = "fuzzerInitialize"; 38 private static final String FUZZER_TEAR_DOWN = "fuzzerTearDown"; 39 findFuzzTargetClassName()40 static String findFuzzTargetClassName() { 41 if (!Opt.targetClass.isEmpty()) { 42 return Opt.targetClass; 43 } 44 if (IS_ANDROID) { 45 // Fuzz target detection tools aren't supported on android 46 return null; 47 } 48 return ManifestUtils.detectFuzzTargetClass(); 49 } 50 51 /** 52 * @throws IllegalArgumentException if the fuzz target method is invalid or couldn't be found 53 * @param targetClassName name of the fuzz target class 54 * @return a {@link FuzzTarget} 55 */ findFuzzTarget(String targetClassName)56 static FuzzTarget findFuzzTarget(String targetClassName) { 57 Class<?> fuzzTargetClass; 58 try { 59 fuzzTargetClass = 60 Class.forName(targetClassName, false, FuzzTargetFinder.class.getClassLoader()); 61 } catch (ClassNotFoundException e) { 62 Log.error(String.format( 63 "'%s' not found on classpath:%n%n%s%n%nAll required classes must be on the classpath specified via --cp.", 64 targetClassName, System.getProperty("java.class.path"))); 65 exit(1); 66 throw new IllegalStateException("Not reached"); 67 } 68 69 return findFuzzTargetByMethodName(fuzzTargetClass); 70 } 71 72 // Finds the traditional static fuzzerTestOneInput fuzz target method. findFuzzTargetByMethodName(Class<?> clazz)73 private static FuzzTarget findFuzzTargetByMethodName(Class<?> clazz) { 74 Method fuzzTargetMethod; 75 if (Opt.experimentalMutator) { 76 List<Method> fuzzTargetMethods = 77 Arrays.stream(clazz.getMethods()) 78 .filter(method -> "fuzzerTestOneInput".equals(method.getName())) 79 .filter(method -> Modifier.isStatic(method.getModifiers())) 80 .collect(Collectors.toList()); 81 if (fuzzTargetMethods.size() != 1) { 82 throw new IllegalArgumentException( 83 String.format("%s must define exactly one function of this form:%n" 84 + "public static void fuzzerTestOneInput(...)%n", 85 clazz.getName())); 86 } 87 fuzzTargetMethod = fuzzTargetMethods.get(0); 88 } else { 89 Optional<Method> bytesFuzzTarget = 90 targetPublicStaticMethod(clazz, FUZZER_TEST_ONE_INPUT, byte[].class); 91 Optional<Method> dataFuzzTarget = 92 targetPublicStaticMethod(clazz, FUZZER_TEST_ONE_INPUT, FuzzedDataProvider.class); 93 if (bytesFuzzTarget.isPresent() == dataFuzzTarget.isPresent()) { 94 throw new IllegalArgumentException(String.format( 95 "%s must define exactly one of the following two functions:%n" 96 + "public static void fuzzerTestOneInput(byte[] ...)%n" 97 + "public static void fuzzerTestOneInput(FuzzedDataProvider ...)%n" 98 + "Note: Fuzz targets returning boolean are no longer supported; exceptions should be thrown instead of returning true.", 99 clazz.getName())); 100 } 101 fuzzTargetMethod = dataFuzzTarget.orElseGet(bytesFuzzTarget::get); 102 } 103 104 Callable<Object> initialize = 105 Stream 106 .of(targetPublicStaticMethod(clazz, FUZZER_INITIALIZE, String[].class) 107 .map(init -> (Callable<Object>) () -> { 108 init.invoke(null, (Object) Opt.targetArgs.toArray(new String[] {})); 109 return null; 110 }), 111 targetPublicStaticMethod(clazz, FUZZER_INITIALIZE) 112 .map(init -> (Callable<Object>) () -> { 113 init.invoke(null); 114 return null; 115 })) 116 .filter(Optional::isPresent) 117 .map(Optional::get) 118 .findFirst() 119 .orElse(() -> null); 120 121 return new FuzzTarget( 122 fuzzTargetMethod, initialize, targetPublicStaticMethod(clazz, FUZZER_TEAR_DOWN)); 123 } 124 targetPublicStaticMethod( Class<?> clazz, String name, Class<?>... parameterTypes)125 private static Optional<Method> targetPublicStaticMethod( 126 Class<?> clazz, String name, Class<?>... parameterTypes) { 127 try { 128 Method method = clazz.getMethod(name, parameterTypes); 129 if (!Modifier.isStatic(method.getModifiers()) || !Modifier.isPublic(method.getModifiers())) { 130 return Optional.empty(); 131 } 132 return Optional.of(method); 133 } catch (NoSuchMethodException e) { 134 return Optional.empty(); 135 } 136 } 137 } 138