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.Constants.JAZZER_VERSION; 20 import static com.code_intelligence.jazzer.driver.OptParser.boolSetting; 21 import static com.code_intelligence.jazzer.driver.OptParser.ignoreSetting; 22 import static com.code_intelligence.jazzer.driver.OptParser.lazyStringListSetting; 23 import static com.code_intelligence.jazzer.driver.OptParser.stringListSetting; 24 import static com.code_intelligence.jazzer.driver.OptParser.stringSetting; 25 import static com.code_intelligence.jazzer.driver.OptParser.uint64Setting; 26 import static java.lang.System.exit; 27 import static java.util.Collections.unmodifiableList; 28 import static java.util.Collections.unmodifiableSet; 29 import static java.util.stream.Collectors.toList; 30 import static java.util.stream.Collectors.toSet; 31 import static java.util.stream.Stream.concat; 32 33 import com.code_intelligence.jazzer.utils.Log; 34 import java.util.List; 35 import java.util.Set; 36 import java.util.function.Supplier; 37 import java.util.stream.Stream; 38 39 /** 40 * Static options that determine the runtime behavior of the fuzzer, set via Java properties. 41 * 42 * <p>Each option corresponds to a command-line argument of the driver of the same name. 43 * 44 * <p>Every public field should be deeply immutable. 45 */ 46 public final class Opt { 47 static { 48 if (Opt.class.getClassLoader() == null) { 49 throw new IllegalStateException("Opt should not be loaded in the bootstrap class loader"); 50 } 51 } 52 53 static { 54 // We additionally list system properties supported by the Jazzer JUnit engine that do not 55 // directly map to arguments. These are not shown in help texts. 56 ignoreSetting("instrument"); 57 ignoreSetting("valueprofile"); 58 // The following arguments are interpreted by the native launcher only. They do appear in the 59 // help text, but aren't read by the driver. 60 stringListSetting("jvm_args", 61 "Arguments to pass to the JVM (separator can be escaped with '\\', native launcher only)"); 62 stringListSetting("additional_jvm_args", 63 "Additional arguments to pass to the JVM (separator can be escaped with '\\', native launcher only)"); 64 stringSetting( 65 "agent_path", null, "Custom path to jazzer_agent_deploy.jar (native launcher only)"); 66 // The following arguments are interpreted by the Jazzer main class directly as they require 67 // starting Jazzer as a subprocess. 68 boolSetting( 69 "asan", false, "Allow fuzzing of native libraries compiled with '-fsanitize=address'"); 70 boolSetting( 71 "ubsan", false, "Allow fuzzing of native libraries compiled with '-fsanitize=undefined'"); 72 boolSetting("native", false, 73 "Allow fuzzing of native libraries compiled with '-fsanitize=fuzzer' (implied by --asan and --ubsan)"); 74 // Options currently used by Android only 75 stringSetting("android_init_options", null, 76 "Which libraries to use when initializing ART (native launcher only)"); 77 boolSetting("hwasan", false, "Allow fuzzing of native libraries compiled with hwasan"); 78 } 79 80 public static final String autofuzz = stringSetting("autofuzz", "", 81 "Fully qualified reference (optionally with a Javadoc-style signature) to a " 82 + "method on the class path to be fuzzed with automatically generated arguments " 83 + "(examples: java.lang.System.out::println, java.lang.String::new(byte[]))"); 84 public static final List<String> autofuzzIgnore = stringListSetting("autofuzz_ignore", ',', 85 "Fully qualified names of exception classes to ignore during fuzzing"); 86 public static final String coverageDump = stringSetting("coverage_dump", "", 87 "Path to write a JaCoCo .exec file to when the fuzzer exits (if non-empty)"); 88 public static final String coverageReport = stringSetting("coverage_report", "", 89 "Path to write a human-readable coverage report to when the fuzzer exits (if non-empty)"); 90 public static final List<String> customHooks = 91 stringListSetting("custom_hooks", "Names of classes to load custom hooks from"); 92 public static final List<String> disabledHooks = stringListSetting("disabled_hooks", 93 "Names of classes from which hooks (custom or built-in) should not be loaded from"); 94 public static final String dumpClassesDir = stringSetting( 95 "dump_classes_dir", "", "Directory to dump instrumented .class files into (if non-empty)"); 96 public static final boolean experimentalMutator = 97 boolSetting("experimental_mutator", false, "Use an experimental structured mutator"); 98 public static final long experimentalCrossOverFrequency = uint64Setting( 99 "experimental_cross_over_frequency", 100, 100 "(Used in experimental mutator) Frequency of cross-over mutations actually being executed " 101 + "when the cross-over function is picked by the underlying fuzzing engine (~1/2 of all mutations), " 102 + "other invocations perform type specific mutations via the experimental mutator. " 103 + "(0 = disabled, 1 = every call, 2 = every other call, etc.)."); 104 public static final boolean hooks = boolSetting( 105 "hooks", true, "Apply fuzzing instrumentation (use 'trace' for finer-grained control)"); 106 public static final String idSyncFile = stringSetting("id_sync_file", null, null); 107 public static final Set<Long> ignore = 108 unmodifiableSet(stringListSetting("ignore", ',', 109 "Hex strings representing deduplication tokens of findings that should be ignored") 110 .stream() 111 .map(token -> Long.parseUnsignedLong(token, 16)) 112 .collect(toSet())); 113 public static final long keepGoing = uint64Setting( 114 "keep_going", 1, "Number of distinct findings after which the fuzzer should stop"); 115 public static final String reproducerPath = stringSetting("reproducer_path", ".", 116 "Directory in which stand-alone Java reproducers are stored for each finding"); 117 public static final String targetClass = stringSetting("target_class", "", 118 "Fully qualified name of the fuzz target class (required unless --autofuzz is specified)"); 119 // Used to disambiguate between multiple methods annotated with @FuzzTest in the target class. 120 public static final String targetMethod = stringSetting("target_method", "", null); 121 public static final List<String> trace = stringListSetting("trace", 122 "Types of instrumentation to apply: cmp, cov, div, gep (disabled by default), indir, native"); 123 124 // When Jazzer is executed from the command line, these settings are potentially modified by 125 // JUnit's AgentConfigurator after the Driver has initialized Opt, which would result in stale 126 // values being read if the settings weren't evaluated lazily. 127 // TODO: Look into making all settings lazy, but verify that their value never changes after they 128 // have been read once. 129 public static final Supplier<List<String>> customHookIncludes = 130 lazyStringListSetting("custom_hook_includes", 131 "Glob patterns matching names of classes to instrument with hooks (custom and built-in)"); 132 public static final Supplier<List<String>> customHookExcludes = lazyStringListSetting( 133 "custom_hook_excludes", 134 "Glob patterns matching names of classes that should not be instrumented with hooks (custom and built-in)"); 135 public static final Supplier<List<String>> instrumentationIncludes = 136 lazyStringListSetting("instrumentation_includes", 137 "Glob patterns matching names of classes to instrument for fuzzing"); 138 public static final Supplier<List<String>> instrumentationExcludes = 139 lazyStringListSetting("instrumentation_excludes", 140 "Glob patterns matching names of classes that should not be instrumented for fuzzing"); 141 // The values of this setting depends on autofuzz. 142 public static final List<String> targetArgs = autofuzz.isEmpty() 143 ? stringListSetting( 144 "target_args", ' ', "Arguments to pass to the fuzz target's fuzzerInitialize method") 145 : unmodifiableList(concat(Stream.of(autofuzz), autofuzzIgnore.stream()).collect(toList())); 146 147 // Default to false if hooks is false to mimic the original behavior of the native fuzz target 148 // runner, but still support hooks = false && dedup = true. 149 public static final boolean dedup = 150 boolSetting("dedup", hooks, "Compute and print a deduplication token for every finding"); 151 152 public static final String androidBootclassJarPath = stringSetting("android_bootclass_jar_path", 153 null, 154 "Full path to booclass jar path that will be used on Android runs. If you are using the launcher this will be set for you."); 155 156 public static final String androidBootclassClassesOverrides = stringSetting( 157 "android_bootpath_classes_overrides", null, 158 "Used for fuzzing classes loaded in through the bootstrap class loader on Android. Full path to jar file with the instrumented versions of the classes you want to override."); 159 160 // Whether hook instrumentation should add a check for JazzerInternal#hooksEnabled before 161 // executing hooks. Used to disable hooks during non-fuzz JUnit tests. 162 public static final boolean conditionalHooks = 163 boolSetting("internal.conditional_hooks", false, null); 164 165 static final boolean mergeInner = boolSetting("internal.merge_inner", false, null); 166 167 private static final boolean help = 168 boolSetting("help", false, "Show this list of all available arguments"); 169 private static final boolean version = boolSetting("version", false, "Print version information"); 170 171 // Methods below currently used by Android only 172 public static final List<String> cp = 173 stringListSetting("cp", "The class path to use for fuzzing (native launcher only)"); 174 175 public static final List<String> additionalClassesExcludes = 176 stringListSetting("additional_classes_excludes", 177 "Glob patterns matching names of classes from Java that are not in your jar file, " 178 + "but may be included in your program"); 179 180 // Default to false. Sets if fuzzing is taking place on Android device (virtual or physical) 181 public static final boolean isAndroid = 182 boolSetting("android", false, "Jazzer is running on Android"); 183 184 // Some scenarios require instrumenting the jar before fuzzing begins 185 public static final List<String> instrumentOnly = stringListSetting("instrument_only", ',', 186 "Comma separated list of jar files to instrument. No fuzzing is performed."); 187 188 static { OptParser.failOnUnknownArgument()189 OptParser.failOnUnknownArgument(); 190 191 if (help) { OptParser.getHelpText()192 Log.println(OptParser.getHelpText()); 193 exit(0); 194 } 195 if (version) { 196 Log.println("Jazzer v" + JAZZER_VERSION); 197 exit(0); 198 } 199 if (!targetClass.isEmpty() && !autofuzz.isEmpty()) { 200 Log.error("--target_class and --autofuzz cannot be specified together"); 201 exit(1); 202 } 203 if (!stringListSetting("target_args", ' ', null).isEmpty() && !autofuzz.isEmpty()) { 204 Log.error("--target_args and --autofuzz cannot be specified together"); 205 exit(1); 206 } 207 if (autofuzz.isEmpty() && !autofuzzIgnore.isEmpty()) { 208 Log.error("--autofuzz_ignore requires --autofuzz"); 209 exit(1); 210 } 211 if ((!ignore.isEmpty() || keepGoing > 1) && !dedup) { 212 Log.error("--nodedup is not supported with --ignore or --keep_going"); 213 exit(1); 214 } 215 if (!instrumentOnly.isEmpty() && dumpClassesDir.isEmpty()) { 216 Log.error("--dump_classes_dir must be set with --instrument_only"); 217 exit(1); 218 } 219 } 220 } 221