xref: /aosp_15_r20/external/jazzer-api/src/main/java/com/code_intelligence/jazzer/driver/Opt.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.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