blob: 78accfd67f880ee551cbb35bad1db0c75bfd83d6 [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.Constants.JAZZER_VERSION;
import static com.code_intelligence.jazzer.driver.OptParser.boolSetting;
import static com.code_intelligence.jazzer.driver.OptParser.ignoreSetting;
import static com.code_intelligence.jazzer.driver.OptParser.lazyStringListSetting;
import static com.code_intelligence.jazzer.driver.OptParser.stringListSetting;
import static com.code_intelligence.jazzer.driver.OptParser.stringSetting;
import static com.code_intelligence.jazzer.driver.OptParser.uint64Setting;
import static java.lang.System.exit;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableSet;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static java.util.stream.Stream.concat;
import com.code_intelligence.jazzer.utils.Log;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* Static options that determine the runtime behavior of the fuzzer, set via Java properties.
*
* <p>Each option corresponds to a command-line argument of the driver of the same name.
*
* <p>Every public field should be deeply immutable.
*/
public final class Opt {
static {
if (Opt.class.getClassLoader() == null) {
throw new IllegalStateException("Opt should not be loaded in the bootstrap class loader");
}
}
static {
// We additionally list system properties supported by the Jazzer JUnit engine that do not
// directly map to arguments. These are not shown in help texts.
ignoreSetting("instrument");
ignoreSetting("valueprofile");
// The following arguments are interpreted by the native launcher only. They do appear in the
// help text, but aren't read by the driver.
stringListSetting("jvm_args",
"Arguments to pass to the JVM (separator can be escaped with '\\', native launcher only)");
stringListSetting("additional_jvm_args",
"Additional arguments to pass to the JVM (separator can be escaped with '\\', native launcher only)");
stringSetting(
"agent_path", null, "Custom path to jazzer_agent_deploy.jar (native launcher only)");
// The following arguments are interpreted by the Jazzer main class directly as they require
// starting Jazzer as a subprocess.
boolSetting(
"asan", false, "Allow fuzzing of native libraries compiled with '-fsanitize=address'");
boolSetting(
"ubsan", false, "Allow fuzzing of native libraries compiled with '-fsanitize=undefined'");
boolSetting("hwasan", false, "Allow fuzzing of native libraries compiled with hwasan");
boolSetting("native", false,
"Allow fuzzing of native libraries compiled with '-fsanitize=fuzzer' (implied by --asan and --ubsan)");
}
public static final List<String> cp =
stringListSetting("cp", "The class path to use for fuzzing (native launcher only)");
public static final String autofuzz = stringSetting("autofuzz", "",
"Fully qualified reference (optionally with a Javadoc-style signature) to a "
+ "method on the class path to be fuzzed with automatically generated arguments "
+ "(examples: java.lang.System.out::println, java.lang.String::new(byte[]))");
public static final List<String> autofuzzIgnore = stringListSetting("autofuzz_ignore", ',',
"Fully qualified names of exception classes to ignore during fuzzing");
public static final String coverageDump = stringSetting("coverage_dump", "",
"Path to write a JaCoCo .exec file to when the fuzzer exits (if non-empty)");
public static final String coverageReport = stringSetting("coverage_report", "",
"Path to write a human-readable coverage report to when the fuzzer exits (if non-empty)");
public static final List<String> customHooks =
stringListSetting("custom_hooks", "Names of classes to load custom hooks from");
public static final List<String> disabledHooks = stringListSetting("disabled_hooks",
"Names of classes from which hooks (custom or built-in) should not be loaded from");
public static final String dumpClassesDir = stringSetting(
"dump_classes_dir", "", "Directory to dump instrumented .class files into (if non-empty)");
public static final boolean experimentalMutator =
boolSetting("experimental_mutator", false, "Use an experimental structured mutator");
public static final long experimentalCrossOverFrequency = uint64Setting(
"experimental_cross_over_frequency", 100,
"(Used in experimental mutator) Frequency of cross-over mutations actually being executed "
+ "when the cross-over function is picked by the underlying fuzzing engine (~1/2 of all mutations), "
+ "other invocations perform type specific mutations via the experimental mutator. "
+ "(0 = disabled, 1 = every call, 2 = every other call, etc.).");
public static final boolean hooks = boolSetting(
"hooks", true, "Apply fuzzing instrumentation (use 'trace' for finer-grained control)");
public static final String idSyncFile = stringSetting("id_sync_file", null, null);
public static final List<String> additionalClassesExcludes =
stringListSetting("additional_classes_excludes",
"Glob patterns matching names of classes from Java that are not in your jar file, "
+ "but may be included in your program");
public static final Set<Long> ignore =
unmodifiableSet(stringListSetting("ignore", ',',
"Hex strings representing deduplication tokens of findings that should be ignored")
.stream()
.map(token -> Long.parseUnsignedLong(token, 16))
.collect(toSet()));
public static final long keepGoing = uint64Setting(
"keep_going", 1, "Number of distinct findings after which the fuzzer should stop");
public static final String reproducerPath = stringSetting("reproducer_path", ".",
"Directory in which stand-alone Java reproducers are stored for each finding");
public static final String targetClass = stringSetting("target_class", "",
"Fully qualified name of the fuzz target class (required unless --autofuzz is specified)");
// Used to disambiguate between multiple methods annotated with @FuzzTest in the target class.
public static final String targetMethod = stringSetting("target_method", "", null);
public static final List<String> trace = stringListSetting("trace",
"Types of instrumentation to apply: cmp, cov, div, gep (disabled by default), indir, native");
// When Jazzer is executed from the command line, these settings are potentially modified by
// JUnit's AgentConfigurator after the Driver has initialized Opt, which would result in stale
// values being read if the settings weren't evaluated lazily.
// TODO: Look into making all settings lazy, but verify that their value never changes after they
// have been read once.
public static final Supplier<List<String>> customHookIncludes =
lazyStringListSetting("custom_hook_includes",
"Glob patterns matching names of classes to instrument with hooks (custom and built-in)");
public static final Supplier<List<String>> customHookExcludes = lazyStringListSetting(
"custom_hook_excludes",
"Glob patterns matching names of classes that should not be instrumented with hooks (custom and built-in)");
public static final Supplier<List<String>> instrumentationIncludes =
lazyStringListSetting("instrumentation_includes",
"Glob patterns matching names of classes to instrument for fuzzing");
public static final Supplier<List<String>> instrumentationExcludes =
lazyStringListSetting("instrumentation_excludes",
"Glob patterns matching names of classes that should not be instrumented for fuzzing");
// The values of this setting depends on autofuzz.
public static final List<String> targetArgs = autofuzz.isEmpty()
? stringListSetting(
"target_args", ' ', "Arguments to pass to the fuzz target's fuzzerInitialize method")
: unmodifiableList(concat(Stream.of(autofuzz), autofuzzIgnore.stream()).collect(toList()));
// Default to false if hooks is false to mimic the original behavior of the native fuzz target
// runner, but still support hooks = false && dedup = true.
public static final boolean dedup =
boolSetting("dedup", hooks, "Compute and print a deduplication token for every finding");
// Whether hook instrumentation should add a check for JazzerInternal#hooksEnabled before
// executing hooks. Used to disable hooks during non-fuzz JUnit tests.
public static final boolean conditionalHooks =
boolSetting("internal.conditional_hooks", false, null);
// Some scenarios require instrumenting the jar before fuzzing begins
public static final List<String> instrumentOnly = stringListSetting("instrument_only", ',',
"Comma separated list of jar files to instrument. No fuzzing is performed.");
static final boolean mergeInner = boolSetting("internal.merge_inner", false, null);
private static final boolean help =
boolSetting("help", false, "Show this list of all available arguments");
private static final boolean version = boolSetting("version", false, "Print version information");
static {
OptParser.failOnUnknownArgument();
if (help) {
Log.println(OptParser.getHelpText());
exit(0);
}
if (version) {
Log.println("Jazzer v" + JAZZER_VERSION);
exit(0);
}
if (!targetClass.isEmpty() && !autofuzz.isEmpty()) {
Log.error("--target_class and --autofuzz cannot be specified together");
exit(1);
}
if (!stringListSetting("target_args", ' ', null).isEmpty() && !autofuzz.isEmpty()) {
Log.error("--target_args and --autofuzz cannot be specified together");
exit(1);
}
if (autofuzz.isEmpty() && !autofuzzIgnore.isEmpty()) {
Log.error("--autofuzz_ignore requires --autofuzz");
exit(1);
}
if ((!ignore.isEmpty() || keepGoing > 1) && !dedup) {
Log.error("--nodedup is not supported with --ignore or --keep_going");
exit(1);
}
if (!instrumentOnly.isEmpty() && dumpClassesDir.isEmpty()) {
Log.error("--dump_classes_dir must be set with --instrument_only");
exit(1);
}
}
}