aboutsummaryrefslogtreecommitdiff
path: root/driver/src/main/java/com/code_intelligence/jazzer/driver/Opt.java
blob: 477c7d38daf3572c1cbb08b5a94106c874ca9fbb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/*
 * 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 java.lang.System.err;
import static java.lang.System.exit;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
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.
 *
 * <p>This class is loaded twice: As it is used in {@link FuzzTargetRunner}, it is loaded in the
 * class loader that loads {@link Driver}. It is also used in
 * {@link com.code_intelligence.jazzer.agent.Agent} after the agent JAR has been added to the
 * bootstrap classpath and thus is loaded again in the bootstrap loader. This is not a problem since
 * it only provides immutable fields and has no non-fatal side effects.
 */
public final class Opt {
  private static final char SYSTEM_DELIMITER =
      System.getProperty("os.name").startsWith("Windows") ? ';' : ':';

  public static final String autofuzz = stringSetting("autofuzz", "");
  public static final List<String> autofuzzIgnore = stringListSetting("autofuzz_ignore", ',');
  public static final String coverageDump = stringSetting("coverage_dump", "");
  public static final String coverageReport = stringSetting("coverage_report", "");
  public static final List<String> customHookIncludes = stringListSetting("custom_hook_includes");
  public static final List<String> customHookExcludes = stringListSetting("custom_hook_excludes");
  public static final List<String> customHooks = stringListSetting("custom_hooks");
  public static final List<String> disabledHooks = stringListSetting("disabled_hooks");
  public static final String dumpClassesDir = stringSetting("dump_classes_dir", "");
  public static final boolean hooks = boolSetting("hooks", true);
  public static final String idSyncFile = stringSetting("id_sync_file", null);
  public static final List<String> instrumentationIncludes =
      stringListSetting("instrumentation_includes");
  public static final List<String> instrumentationExcludes =
      stringListSetting("instrumentation_excludes");
  public static final Set<Long> ignore =
      Collections.unmodifiableSet(stringListSetting("ignore", ',')
                                      .stream()
                                      .map(Long::parseUnsignedLong)
                                      .collect(Collectors.toSet()));
  public static final String reproducerPath = stringSetting("reproducer_path", ".");
  public static final String targetClass = stringSetting("target_class", "");
  public static final List<String> trace = stringListSetting("trace");

  // The values of these settings depend on autofuzz.
  public static final List<String> targetArgs = autofuzz.isEmpty()
      ? stringListSetting("target_args", ' ')
      : Collections.unmodifiableList(
          Stream.concat(Stream.of(autofuzz), autofuzzIgnore.stream()).collect(Collectors.toList()));
  public static final long keepGoing =
      uint64Setting("keep_going", autofuzz.isEmpty() ? 1 : Long.MAX_VALUE);

  // 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);

  static {
    if (!targetClass.isEmpty() && !autofuzz.isEmpty()) {
      err.println("--target_class and --autofuzz cannot be specified together");
      exit(1);
    }
    if (!stringListSetting("target_args", ' ').isEmpty() && !autofuzz.isEmpty()) {
      err.println("--target_args and --autofuzz cannot be specified together");
      exit(1);
    }
    if (autofuzz.isEmpty() && !autofuzzIgnore.isEmpty()) {
      err.println("--autofuzz_ignore requires --autofuzz");
      exit(1);
    }
    if ((!ignore.isEmpty() || keepGoing > 1) && !dedup) {
      // --autofuzz implicitly sets keepGoing to Integer.MAX_VALUE.
      err.println("--nodedup is not supported with --ignore, --keep_going, or --autofuzz");
      exit(1);
    }
  }

  private static final String optionsPrefix = "jazzer.";

  private static String stringSetting(String name, String defaultValue) {
    return System.getProperty(optionsPrefix + name, defaultValue);
  }

  private static List<String> stringListSetting(String name) {
    return stringListSetting(name, SYSTEM_DELIMITER);
  }

  private static List<String> stringListSetting(String name, char separator) {
    String value = System.getProperty(optionsPrefix + name);
    if (value == null || value.isEmpty()) {
      return Collections.emptyList();
    }
    return splitOnUnescapedSeparator(value, separator);
  }

  private static boolean boolSetting(String name, boolean defaultValue) {
    String value = System.getProperty(optionsPrefix + name);
    if (value == null) {
      return defaultValue;
    }
    return Boolean.parseBoolean(value);
  }

  private static long uint64Setting(String name, long defaultValue) {
    String value = System.getProperty(optionsPrefix + name);
    if (value == null) {
      return defaultValue;
    }
    return Long.parseUnsignedLong(value, 10);
  }

  /**
   * Split value into non-empty takens separated by separator. Backslashes can be used to escape
   * separators (or backslashes).
   *
   * @param value the string to split
   * @param separator a single character to split on (backslash is not allowed)
   * @return an immutable list of tokens obtained by splitting value on separator
   */
  static List<String> splitOnUnescapedSeparator(String value, char separator) {
    if (separator == '\\') {
      throw new IllegalArgumentException("separator '\\' is not supported");
    }
    ArrayList<String> tokens = new ArrayList<>();
    StringBuilder currentToken = new StringBuilder();
    boolean inEscapeState = false;
    for (int pos = 0; pos < value.length(); pos++) {
      char c = value.charAt(pos);
      if (inEscapeState) {
        currentToken.append(c);
        inEscapeState = false;
      } else if (c == '\\') {
        inEscapeState = true;
      } else if (c == separator) {
        // Do not emit empty tokens between consecutive separators.
        if (currentToken.length() > 0) {
          tokens.add(currentToken.toString());
        }
        currentToken.setLength(0);
      } else {
        currentToken.append(c);
      }
    }
    if (currentToken.length() > 0) {
      tokens.add(currentToken.toString());
    }
    return Collections.unmodifiableList(tokens);
  }
}