blob: c2c41774c3c82a2ca020fff6045d41083d11de4b [file] [log] [blame]
/*
* Copyright 2022 Code Intelligence GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.code_intelligence.jazzer.driver;
import static com.code_intelligence.jazzer.runtime.Constants.IS_ANDROID;
import static java.lang.System.exit;
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.code_intelligence.jazzer.driver.FuzzTargetHolder.FuzzTarget;
import com.code_intelligence.jazzer.utils.Log;
import com.code_intelligence.jazzer.utils.ManifestUtils;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class FuzzTargetFinder {
private static final String FUZZER_TEST_ONE_INPUT = "fuzzerTestOneInput";
private static final String FUZZER_INITIALIZE = "fuzzerInitialize";
private static final String FUZZER_TEAR_DOWN = "fuzzerTearDown";
static String findFuzzTargetClassName() {
if (!Opt.targetClass.isEmpty()) {
return Opt.targetClass;
}
if (IS_ANDROID) {
// Fuzz target detection tools aren't supported on android
return null;
}
return ManifestUtils.detectFuzzTargetClass();
}
/**
* @throws IllegalArgumentException if the fuzz target method is invalid or couldn't be found
* @param targetClassName name of the fuzz target class
* @return a {@link FuzzTarget}
*/
static FuzzTarget findFuzzTarget(String targetClassName) {
Class<?> fuzzTargetClass;
try {
fuzzTargetClass =
Class.forName(targetClassName, false, FuzzTargetFinder.class.getClassLoader());
} catch (ClassNotFoundException e) {
Log.error(String.format(
"'%s' not found on classpath:%n%n%s%n%nAll required classes must be on the classpath specified via --cp.",
targetClassName, System.getProperty("java.class.path")));
exit(1);
throw new IllegalStateException("Not reached");
}
return findFuzzTargetByMethodName(fuzzTargetClass);
}
// Finds the traditional static fuzzerTestOneInput fuzz target method.
private static FuzzTarget findFuzzTargetByMethodName(Class<?> clazz) {
Method fuzzTargetMethod;
if (Opt.experimentalMutator) {
List<Method> fuzzTargetMethods =
Arrays.stream(clazz.getMethods())
.filter(method -> "fuzzerTestOneInput".equals(method.getName()))
.filter(method -> Modifier.isStatic(method.getModifiers()))
.collect(Collectors.toList());
if (fuzzTargetMethods.size() != 1) {
throw new IllegalArgumentException(
String.format("%s must define exactly one function of this form:%n"
+ "public static void fuzzerTestOneInput(...)%n",
clazz.getName()));
}
fuzzTargetMethod = fuzzTargetMethods.get(0);
} else {
Optional<Method> bytesFuzzTarget =
targetPublicStaticMethod(clazz, FUZZER_TEST_ONE_INPUT, byte[].class);
Optional<Method> dataFuzzTarget =
targetPublicStaticMethod(clazz, FUZZER_TEST_ONE_INPUT, FuzzedDataProvider.class);
if (bytesFuzzTarget.isPresent() == dataFuzzTarget.isPresent()) {
throw new IllegalArgumentException(String.format(
"%s must define exactly one of the following two functions:%n"
+ "public static void fuzzerTestOneInput(byte[] ...)%n"
+ "public static void fuzzerTestOneInput(FuzzedDataProvider ...)%n"
+ "Note: Fuzz targets returning boolean are no longer supported; exceptions should be thrown instead of returning true.",
clazz.getName()));
}
fuzzTargetMethod = dataFuzzTarget.orElseGet(bytesFuzzTarget::get);
}
Callable<Object> initialize =
Stream
.of(targetPublicStaticMethod(clazz, FUZZER_INITIALIZE, String[].class)
.map(init -> (Callable<Object>) () -> {
init.invoke(null, (Object) Opt.targetArgs.toArray(new String[] {}));
return null;
}),
targetPublicStaticMethod(clazz, FUZZER_INITIALIZE)
.map(init -> (Callable<Object>) () -> {
init.invoke(null);
return null;
}))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst()
.orElse(() -> null);
return new FuzzTarget(
fuzzTargetMethod, initialize, targetPublicStaticMethod(clazz, FUZZER_TEAR_DOWN));
}
private static Optional<Method> targetPublicStaticMethod(
Class<?> clazz, String name, Class<?>... parameterTypes) {
try {
Method method = clazz.getMethod(name, parameterTypes);
if (!Modifier.isStatic(method.getModifiers()) || !Modifier.isPublic(method.getModifiers())) {
return Optional.empty();
}
return Optional.of(method);
} catch (NoSuchMethodException e) {
return Optional.empty();
}
}
}