blob: bfae5dd834d3f32d4e2e87b3241d4858d2a1608d [file] [log] [blame]
Fabian Meumertzheim678ceb52022-07-28 18:58:47 +02001/*
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
17package com.code_intelligence.jazzer.driver;
18
19import static java.lang.System.err;
20import static java.lang.System.exit;
21
22import java.util.ArrayList;
23import java.util.Collections;
24import java.util.List;
25import java.util.Set;
26import java.util.stream.Collectors;
27import java.util.stream.Stream;
28
29/**
30 * Static options that determine the runtime behavior of the fuzzer, set via Java properties.
31 *
32 * <p>Each option corresponds to a command-line argument of the driver of the same name.
33 *
34 * <p>Every public field should be deeply immutable.
Fabian Meumertzheim16b8e6b2022-08-16 14:43:56 +020035 *
36 * <p>This class is loaded twice: As it is used in {@link FuzzTargetRunner}, it is loaded in the
37 * class loader that loads {@link Driver}. It is also used in
38 * {@link com.code_intelligence.jazzer.agent.Agent} after the agent JAR has been added to the
39 * bootstrap classpath and thus is loaded again in the bootstrap loader. This is not a problem since
40 * it only provides immutable fields and has no non-fatal side effects.
Fabian Meumertzheim678ceb52022-07-28 18:58:47 +020041 */
Fabian Meumertzheim07ce6172022-08-11 10:44:16 +020042public final class Opt {
Fabian Meumertzheim193908f2022-08-11 11:38:38 +020043 private static final char SYSTEM_DELIMITER =
44 System.getProperty("os.name").startsWith("Windows") ? ';' : ':';
45
Fabian Meumertzheim678ceb52022-07-28 18:58:47 +020046 public static final String autofuzz = stringSetting("autofuzz", "");
47 public static final List<String> autofuzzIgnore = stringListSetting("autofuzz_ignore", ',');
48 public static final String coverageDump = stringSetting("coverage_dump", "");
49 public static final String coverageReport = stringSetting("coverage_report", "");
Fabian Meumertzheim193908f2022-08-11 11:38:38 +020050 public static final List<String> customHookIncludes = stringListSetting("custom_hook_includes");
51 public static final List<String> customHookExcludes = stringListSetting("custom_hook_excludes");
52 public static final List<String> customHooks = stringListSetting("custom_hooks");
53 public static final List<String> disabledHooks = stringListSetting("disabled_hooks");
54 public static final String dumpClassesDir = stringSetting("dump_classes_dir", "");
Fabian Meumertzheim678ceb52022-07-28 18:58:47 +020055 public static final boolean hooks = boolSetting("hooks", true);
Fabian Meumertzheim5a2137e2022-08-11 10:00:30 +020056 public static final String idSyncFile = stringSetting("id_sync_file", null);
Fabian Meumertzheim193908f2022-08-11 11:38:38 +020057 public static final List<String> instrumentationIncludes =
58 stringListSetting("instrumentation_includes");
59 public static final List<String> instrumentationExcludes =
60 stringListSetting("instrumentation_excludes");
Fabian Meumertzheim678ceb52022-07-28 18:58:47 +020061 public static final Set<Long> ignore =
62 Collections.unmodifiableSet(stringListSetting("ignore", ',')
63 .stream()
64 .map(Long::parseUnsignedLong)
65 .collect(Collectors.toSet()));
Fabian Meumertzheim678ceb52022-07-28 18:58:47 +020066 public static final String reproducerPath = stringSetting("reproducer_path", ".");
Fabian Meumertzheim193908f2022-08-11 11:38:38 +020067 public static final String targetClass = stringSetting("target_class", "");
Fabian Meumertzheim01d548e2022-08-26 23:35:51 +020068 // Used to disambiguate between multiple methods annotated with @FuzzTest in the target class.
69 public static final String targetMethod = stringSetting("target_method", "");
Fabian Meumertzheim193908f2022-08-11 11:38:38 +020070 public static final List<String> trace = stringListSetting("trace");
Fabian Meumertzheim678ceb52022-07-28 18:58:47 +020071
72 // The values of these settings depend on autofuzz.
73 public static final List<String> targetArgs = autofuzz.isEmpty()
74 ? stringListSetting("target_args", ' ')
75 : Collections.unmodifiableList(
76 Stream.concat(Stream.of(autofuzz), autofuzzIgnore.stream()).collect(Collectors.toList()));
77 public static final long keepGoing =
Fabian Meumertzheim6ee1c1c2022-08-18 10:11:50 +020078 uint64Setting("keep_going", autofuzz.isEmpty() ? 1 : Long.MAX_VALUE);
Fabian Meumertzheim678ceb52022-07-28 18:58:47 +020079
Fabian Meumertzheim193908f2022-08-11 11:38:38 +020080 // Default to false if hooks is false to mimic the original behavior of the native fuzz target
81 // runner, but still support hooks = false && dedup = true.
Fabian Meumertzheimc90c0732022-08-11 12:22:10 +020082 public static final boolean dedup = boolSetting("dedup", hooks);
Fabian Meumertzheim193908f2022-08-11 11:38:38 +020083
Fabian Meumertzheim678ceb52022-07-28 18:58:47 +020084 static {
85 if (!targetClass.isEmpty() && !autofuzz.isEmpty()) {
86 err.println("--target_class and --autofuzz cannot be specified together");
87 exit(1);
88 }
89 if (!stringListSetting("target_args", ' ').isEmpty() && !autofuzz.isEmpty()) {
90 err.println("--target_args and --autofuzz cannot be specified together");
91 exit(1);
92 }
93 if (autofuzz.isEmpty() && !autofuzzIgnore.isEmpty()) {
94 err.println("--autofuzz_ignore requires --autofuzz");
95 exit(1);
96 }
97 if ((!ignore.isEmpty() || keepGoing > 1) && !dedup) {
98 // --autofuzz implicitly sets keepGoing to Integer.MAX_VALUE.
99 err.println("--nodedup is not supported with --ignore, --keep_going, or --autofuzz");
100 exit(1);
101 }
102 }
103
104 private static final String optionsPrefix = "jazzer.";
105
106 private static String stringSetting(String name, String defaultValue) {
107 return System.getProperty(optionsPrefix + name, defaultValue);
108 }
109
Fabian Meumertzheim193908f2022-08-11 11:38:38 +0200110 private static List<String> stringListSetting(String name) {
111 return stringListSetting(name, SYSTEM_DELIMITER);
112 }
113
Fabian Meumertzheim678ceb52022-07-28 18:58:47 +0200114 private static List<String> stringListSetting(String name, char separator) {
115 String value = System.getProperty(optionsPrefix + name);
116 if (value == null || value.isEmpty()) {
117 return Collections.emptyList();
118 }
119 return splitOnUnescapedSeparator(value, separator);
120 }
121
122 private static boolean boolSetting(String name, boolean defaultValue) {
123 String value = System.getProperty(optionsPrefix + name);
124 if (value == null) {
125 return defaultValue;
126 }
127 return Boolean.parseBoolean(value);
128 }
129
Fabian Meumertzheim6ee1c1c2022-08-18 10:11:50 +0200130 private static long uint64Setting(String name, long defaultValue) {
Fabian Meumertzheim678ceb52022-07-28 18:58:47 +0200131 String value = System.getProperty(optionsPrefix + name);
132 if (value == null) {
133 return defaultValue;
134 }
Fabian Meumertzheim6ee1c1c2022-08-18 10:11:50 +0200135 return Long.parseUnsignedLong(value, 10);
Fabian Meumertzheim678ceb52022-07-28 18:58:47 +0200136 }
137
138 /**
139 * Split value into non-empty takens separated by separator. Backslashes can be used to escape
140 * separators (or backslashes).
141 *
142 * @param value the string to split
143 * @param separator a single character to split on (backslash is not allowed)
144 * @return an immutable list of tokens obtained by splitting value on separator
145 */
146 static List<String> splitOnUnescapedSeparator(String value, char separator) {
147 if (separator == '\\') {
148 throw new IllegalArgumentException("separator '\\' is not supported");
149 }
150 ArrayList<String> tokens = new ArrayList<>();
151 StringBuilder currentToken = new StringBuilder();
152 boolean inEscapeState = false;
153 for (int pos = 0; pos < value.length(); pos++) {
154 char c = value.charAt(pos);
155 if (inEscapeState) {
156 currentToken.append(c);
157 inEscapeState = false;
158 } else if (c == '\\') {
159 inEscapeState = true;
160 } else if (c == separator) {
161 // Do not emit empty tokens between consecutive separators.
162 if (currentToken.length() > 0) {
163 tokens.add(currentToken.toString());
164 }
165 currentToken.setLength(0);
166 } else {
167 currentToken.append(c);
168 }
169 }
170 if (currentToken.length() > 0) {
171 tokens.add(currentToken.toString());
172 }
173 return Collections.unmodifiableList(tokens);
174 }
175}