xref: /aosp_15_r20/external/jazzer-api/src/main/java/com/code_intelligence/jazzer/driver/FuzzTargetFinder.java (revision 33edd6723662ea34453766bfdca85dbfdd5342b8)
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