Fabian Meumertzheim | 678ceb5 | 2022-07-28 18:58:47 +0200 | [diff] [blame] | 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 java.lang.System.err; |
| 20 | import static java.lang.System.exit; |
| 21 | |
| 22 | import java.util.ArrayList; |
| 23 | import java.util.Collections; |
| 24 | import java.util.List; |
| 25 | import java.util.Set; |
| 26 | import java.util.stream.Collectors; |
| 27 | import 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 Meumertzheim | 16b8e6b | 2022-08-16 14:43:56 +0200 | [diff] [blame] | 35 | * |
| 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 Meumertzheim | 678ceb5 | 2022-07-28 18:58:47 +0200 | [diff] [blame] | 41 | */ |
Fabian Meumertzheim | 07ce617 | 2022-08-11 10:44:16 +0200 | [diff] [blame] | 42 | public final class Opt { |
Fabian Meumertzheim | 193908f | 2022-08-11 11:38:38 +0200 | [diff] [blame] | 43 | private static final char SYSTEM_DELIMITER = |
| 44 | System.getProperty("os.name").startsWith("Windows") ? ';' : ':'; |
| 45 | |
Fabian Meumertzheim | 678ceb5 | 2022-07-28 18:58:47 +0200 | [diff] [blame] | 46 | 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 Meumertzheim | 193908f | 2022-08-11 11:38:38 +0200 | [diff] [blame] | 50 | 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 Meumertzheim | 678ceb5 | 2022-07-28 18:58:47 +0200 | [diff] [blame] | 55 | public static final boolean hooks = boolSetting("hooks", true); |
Fabian Meumertzheim | 5a2137e | 2022-08-11 10:00:30 +0200 | [diff] [blame] | 56 | public static final String idSyncFile = stringSetting("id_sync_file", null); |
Fabian Meumertzheim | 193908f | 2022-08-11 11:38:38 +0200 | [diff] [blame] | 57 | public static final List<String> instrumentationIncludes = |
| 58 | stringListSetting("instrumentation_includes"); |
| 59 | public static final List<String> instrumentationExcludes = |
| 60 | stringListSetting("instrumentation_excludes"); |
Fabian Meumertzheim | 678ceb5 | 2022-07-28 18:58:47 +0200 | [diff] [blame] | 61 | public static final Set<Long> ignore = |
| 62 | Collections.unmodifiableSet(stringListSetting("ignore", ',') |
| 63 | .stream() |
| 64 | .map(Long::parseUnsignedLong) |
| 65 | .collect(Collectors.toSet())); |
Fabian Meumertzheim | 678ceb5 | 2022-07-28 18:58:47 +0200 | [diff] [blame] | 66 | public static final String reproducerPath = stringSetting("reproducer_path", "."); |
Fabian Meumertzheim | 193908f | 2022-08-11 11:38:38 +0200 | [diff] [blame] | 67 | public static final String targetClass = stringSetting("target_class", ""); |
Fabian Meumertzheim | 01d548e | 2022-08-26 23:35:51 +0200 | [diff] [blame^] | 68 | // Used to disambiguate between multiple methods annotated with @FuzzTest in the target class. |
| 69 | public static final String targetMethod = stringSetting("target_method", ""); |
Fabian Meumertzheim | 193908f | 2022-08-11 11:38:38 +0200 | [diff] [blame] | 70 | public static final List<String> trace = stringListSetting("trace"); |
Fabian Meumertzheim | 678ceb5 | 2022-07-28 18:58:47 +0200 | [diff] [blame] | 71 | |
| 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 Meumertzheim | 6ee1c1c | 2022-08-18 10:11:50 +0200 | [diff] [blame] | 78 | uint64Setting("keep_going", autofuzz.isEmpty() ? 1 : Long.MAX_VALUE); |
Fabian Meumertzheim | 678ceb5 | 2022-07-28 18:58:47 +0200 | [diff] [blame] | 79 | |
Fabian Meumertzheim | 193908f | 2022-08-11 11:38:38 +0200 | [diff] [blame] | 80 | // 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 Meumertzheim | c90c073 | 2022-08-11 12:22:10 +0200 | [diff] [blame] | 82 | public static final boolean dedup = boolSetting("dedup", hooks); |
Fabian Meumertzheim | 193908f | 2022-08-11 11:38:38 +0200 | [diff] [blame] | 83 | |
Fabian Meumertzheim | 678ceb5 | 2022-07-28 18:58:47 +0200 | [diff] [blame] | 84 | 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 Meumertzheim | 193908f | 2022-08-11 11:38:38 +0200 | [diff] [blame] | 110 | private static List<String> stringListSetting(String name) { |
| 111 | return stringListSetting(name, SYSTEM_DELIMITER); |
| 112 | } |
| 113 | |
Fabian Meumertzheim | 678ceb5 | 2022-07-28 18:58:47 +0200 | [diff] [blame] | 114 | 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 Meumertzheim | 6ee1c1c | 2022-08-18 10:11:50 +0200 | [diff] [blame] | 130 | private static long uint64Setting(String name, long defaultValue) { |
Fabian Meumertzheim | 678ceb5 | 2022-07-28 18:58:47 +0200 | [diff] [blame] | 131 | String value = System.getProperty(optionsPrefix + name); |
| 132 | if (value == null) { |
| 133 | return defaultValue; |
| 134 | } |
Fabian Meumertzheim | 6ee1c1c | 2022-08-18 10:11:50 +0200 | [diff] [blame] | 135 | return Long.parseUnsignedLong(value, 10); |
Fabian Meumertzheim | 678ceb5 | 2022-07-28 18:58:47 +0200 | [diff] [blame] | 136 | } |
| 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 | } |