Skip to content

Commit 55eb18b

Browse files
committed
tests: Verify emitted warnings and errors
1 parent 93ce1ed commit 55eb18b

File tree

7 files changed

+99
-30
lines changed

7 files changed

+99
-30
lines changed

bazel/fuzz_target.bzl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ def java_fuzz_target_test(
3535
allowed_findings = [],
3636
# By default, expect a crash iff allowed_findings isn't empty.
3737
expect_crash = None,
38+
# If empty, expect no warnings or errors, if not empty, expect one matching the given regex.
39+
expected_warning_or_error = "",
3840
**kwargs):
3941
if target_class:
4042
fuzzer_args = fuzzer_args + ["--target_class=" + target_class]
@@ -115,6 +117,7 @@ def java_fuzz_target_test(
115117
str(verify_crash_reproducer),
116118
str(expect_crash),
117119
str(launcher_variant == "java"),
120+
"'" + expected_warning_or_error + "'",
118121
"'" + ",".join(allowed_findings) + "'",
119122
] + fuzzer_args,
120123
data = [

bazel/tools/java/com/code_intelligence/jazzer/tools/FuzzTargetTestWrapper.java

Lines changed: 78 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
// limitations under the License.
1414
package com.code_intelligence.jazzer.tools;
1515

16+
import static java.util.Arrays.asList;
17+
import static java.util.Collections.unmodifiableSet;
1618
import static java.util.stream.Collectors.toList;
19+
import static java.util.stream.Collectors.toSet;
1720

1821
import com.google.devtools.build.runfiles.AutoBazelRepository;
1922
import com.google.devtools.build.runfiles.Runfiles;
@@ -35,6 +38,7 @@
3538
import java.util.Collections;
3639
import java.util.Comparator;
3740
import java.util.List;
41+
import java.util.Optional;
3842
import java.util.Set;
3943
import java.util.regex.Pattern;
4044
import java.util.stream.Collectors;
@@ -47,13 +51,15 @@
4751

4852
@AutoBazelRepository
4953
public class FuzzTargetTestWrapper {
54+
private static final Set<String> IGNORED_WARNINGS =
55+
// Triggered by BatikTranscoderFuzzer on macOS in Github Actions.
56+
Collections.singleton("WARNING: GL pipe is running in software mode (Renderer ID=0x1020400)");
5057
private static final String EXCEPTION_PREFIX = "== Java Exception: ";
5158
private static final String FRAME_PREFIX = "\tat ";
5259
private static final Pattern SANITIZER_FINDING = Pattern.compile("^SUMMARY: \\w*Sanitizer");
5360
private static final String THREAD_DUMP_HEADER = "Stack traces of all JVM threads:";
5461
private static final Set<String> PUBLIC_JAZZER_PACKAGES =
55-
Collections.unmodifiableSet(
56-
Stream.of("api", "replay", "sanitizers").collect(Collectors.toSet()));
62+
unmodifiableSet(Stream.of("api", "replay", "sanitizers").collect(toSet()));
5763

5864
public static void main(String[] args) {
5965
Runfiles runfiles;
@@ -65,6 +71,7 @@ public static void main(String[] args) {
6571
boolean shouldVerifyCrashReproducer;
6672
boolean expectCrash;
6773
boolean usesJavaLauncher;
74+
Optional<String> expectedWarningOrError;
6875
Set<String> allowedFindings;
6976
List<String> arguments;
7077
try {
@@ -78,12 +85,17 @@ public static void main(String[] args) {
7885
shouldVerifyCrashReproducer = Boolean.parseBoolean(args[5]);
7986
expectCrash = Boolean.parseBoolean(args[6]);
8087
usesJavaLauncher = Boolean.parseBoolean(args[7]);
88+
if (args[8].isEmpty()) {
89+
expectedWarningOrError = Optional.empty();
90+
} else {
91+
expectedWarningOrError = Optional.of(args[8]);
92+
}
8193
allowedFindings =
82-
Arrays.stream(args[8].split(",")).filter(s -> !s.isEmpty()).collect(Collectors.toSet());
94+
Arrays.stream(args[9].split(",")).filter(s -> !s.isEmpty()).collect(toSet());
8395
// Map all files/dirs to real location
8496
arguments =
8597
Arrays.stream(args)
86-
.skip(9)
98+
.skip(10)
8799
.map(arg -> arg.startsWith("-") ? arg : runfiles.rlocation(arg))
88100
.collect(toList());
89101
} catch (IOException | ArrayIndexOutOfBoundsException e) {
@@ -150,9 +162,14 @@ public static void main(String[] args) {
150162

151163
try {
152164
Process process = processBuilder.start();
165+
boolean sawErrorWithStackTrace;
153166
try {
154-
verifyFuzzerOutput(
155-
process.getErrorStream(), allowedFindings, arguments.contains("--nohooks"));
167+
sawErrorWithStackTrace =
168+
verifyFuzzerOutput(
169+
process.getErrorStream(),
170+
allowedFindings,
171+
arguments.contains("--nohooks"),
172+
expectedWarningOrError);
156173
} finally {
157174
process.getErrorStream().close();
158175
}
@@ -166,10 +183,11 @@ public static void main(String[] args) {
166183
System.exit(0);
167184
}
168185
// Assert that we either found a crash in Java (exit code 77), a sanitizer crash (exit code
169-
// 76), or a timeout (exit code 70).
186+
// 76), a timeout (exit code 70) or an error with stack trace (exit code 1).
170187
if (exitCode != 76
171188
&& exitCode != 77
172-
&& !(allowedFindings.contains("timeout") && exitCode == 70)) {
189+
&& !(allowedFindings.contains("timeout") && exitCode == 70)
190+
&& !(sawErrorWithStackTrace && exitCode == 1)) {
173191
System.err.printf("Did expect a crash, but Jazzer exited with exit code %d%n", exitCode);
174192
System.exit(1);
175193
}
@@ -207,25 +225,58 @@ public static void main(String[] args) {
207225
System.exit(0);
208226
}
209227

210-
private static void verifyFuzzerOutput(
211-
InputStream fuzzerOutput, Set<String> expectedFindings, boolean noHooks) throws IOException {
212-
List<String> stackTrace;
228+
// Returns true if the fuzzer failed with an error and there was a stack trace.
229+
private static boolean verifyFuzzerOutput(
230+
InputStream fuzzerOutput,
231+
Set<String> expectedFindings,
232+
boolean noHooks,
233+
Optional<String> expectedWarningOrError)
234+
throws IOException {
235+
List<String> lines;
213236
try (BufferedReader reader = new BufferedReader(new InputStreamReader(fuzzerOutput))) {
214-
stackTrace =
215-
reader
216-
.lines()
217-
.peek(System.err::println)
218-
.filter(
219-
line ->
220-
line.startsWith(EXCEPTION_PREFIX)
221-
|| line.startsWith(FRAME_PREFIX)
222-
|| line.equals(THREAD_DUMP_HEADER)
223-
|| SANITIZER_FINDING.matcher(line).find())
224-
.collect(toList());
237+
lines = reader.lines().collect(toList());
238+
}
239+
240+
List<String> warningsAndErrors =
241+
lines.stream()
242+
.filter(line -> line.startsWith("WARN") || line.startsWith("ERROR"))
243+
.filter(line -> !IGNORED_WARNINGS.contains(line))
244+
.collect(toList());
245+
boolean sawError = warningsAndErrors.stream().anyMatch(line -> line.startsWith("ERROR"));
246+
if (!expectedWarningOrError.isPresent() && !warningsAndErrors.isEmpty()) {
247+
throw new IllegalStateException(
248+
"Did not expect warnings or errors, but got:\n" + String.join("\n", warningsAndErrors));
249+
}
250+
if (expectedWarningOrError.isPresent()) {
251+
if (warningsAndErrors.isEmpty()) {
252+
throw new IllegalStateException("Expected a warning or error, but did not get any");
253+
}
254+
String unexpectedWarningsAndErrors =
255+
warningsAndErrors.stream()
256+
.filter(line -> !expectedWarningOrError.get().equals(line))
257+
.collect(Collectors.joining("\n"));
258+
if (!unexpectedWarningsAndErrors.isEmpty()) {
259+
throw new IllegalStateException(
260+
"Got unexpected warnings or errors: " + unexpectedWarningsAndErrors);
261+
}
225262
}
263+
264+
List<String> stackTrace =
265+
lines.stream()
266+
.peek(System.err::println)
267+
.filter(
268+
line ->
269+
line.startsWith(EXCEPTION_PREFIX)
270+
|| line.startsWith(FRAME_PREFIX)
271+
|| line.equals(THREAD_DUMP_HEADER)
272+
|| SANITIZER_FINDING.matcher(line).find())
273+
.collect(toList());
226274
if (expectedFindings.isEmpty()) {
227275
if (stackTrace.isEmpty()) {
228-
return;
276+
return false;
277+
}
278+
if (!warningsAndErrors.isEmpty()) {
279+
return sawError;
229280
}
230281
throw new IllegalStateException(
231282
String.format(
@@ -244,7 +295,7 @@ private static void verifyFuzzerOutput(
244295
if (expectedFindings.size() != 1) {
245296
throw new IllegalStateException("Cannot expect both a native and other findings");
246297
}
247-
return;
298+
return false;
248299
}
249300
if (expectedFindings.contains("timeout")) {
250301
if (!stackTrace.contains(THREAD_DUMP_HEADER) || stackTrace.size() < 3) {
@@ -254,7 +305,7 @@ private static void verifyFuzzerOutput(
254305
if (expectedFindings.size() != 1) {
255306
throw new IllegalStateException("Cannot expect both a timeout and other findings");
256307
}
257-
return;
308+
return false;
258309
}
259310
List<String> findings =
260311
stackTrace.stream()
@@ -291,6 +342,7 @@ private static void verifyFuzzerOutput(
291342
"Unexpected strack trace frames:%n%n%s%n%nin:%n%s",
292343
String.join("\n", unexpectedFrames), String.join("\n", stackTrace)));
293344
}
345+
return false;
294346
}
295347

296348
private static void verifyCrashReproducer(
@@ -313,7 +365,7 @@ private static String compile(File source, Path api, Path targetJar) throws IOEx
313365
try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) {
314366
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(source);
315367
List<String> options =
316-
Arrays.asList(
368+
asList(
317369
"-classpath", String.join(File.pathSeparator, api.toString(), targetJar.toString()));
318370
System.out.printf(
319371
"Compile crash reproducer %s with options %s%n", source.getAbsolutePath(), options);

examples/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ java_fuzz_target_test(
5757
allowed_findings = ["native"],
5858
env = {"EXAMPLE_NATIVE_LIB": "native_asan"},
5959
env_inherit = ["CC"],
60+
expected_warning_or_error = "WARN: Jazzer is not compatible with LeakSanitizer. Leaks are not reported.",
6061
fuzzer_args = [
6162
"--asan",
6263
],
@@ -351,6 +352,7 @@ java_fuzz_target_test(
351352
"src/main/java/com/example/FastJsonFuzzer.java",
352353
],
353354
allowed_findings = ["java.lang.NumberFormatException"],
355+
expected_warning_or_error = "WARN: Some hooks could not be applied to class files built for Java 7 or lower.",
354356
target_class = "com.example.FastJsonFuzzer",
355357
deps = [
356358
"@maven//:com_alibaba_fastjson",
@@ -376,6 +378,7 @@ java_fuzz_target_test(
376378
"java.lang.NumberFormatException",
377379
"java.lang.NullPointerException",
378380
],
381+
expected_warning_or_error = "WARN: Some hooks could not be applied to class files built for Java 7 or lower.",
379382
fuzzer_args = [
380383
"--keep_going=7",
381384
],

examples/junit/src/test/java/com/example/BUILD.bazel

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,13 @@ java_fuzz_target_test(
7272
java_fuzz_target_test(
7373
name = "PerExecutionLifecycleFuzzTest",
7474
srcs = ["PerExecutionLifecycleFuzzTest.java"],
75-
allowed_findings = ["com.example.TestSuccessfulException"],
75+
expect_crash = True,
76+
expected_warning_or_error = "ERROR: com.example.TestSuccessfulException: Lifecycle methods invoked as expected",
7677
fuzzer_args = [
7778
"-runs=3",
7879
],
7980
target_class = "com.example.PerExecutionLifecycleFuzzTest",
81+
verify_crash_input = False,
8082
verify_crash_reproducer = False,
8183
runtime_deps = [
8284
":junit_runtime",
@@ -94,8 +96,8 @@ java_fuzz_target_test(
9496
java_fuzz_target_test(
9597
name = "PerExecutionLifecycleWithFindingFuzzTest",
9698
srcs = ["PerExecutionLifecycleWithFindingFuzzTest.java"],
97-
# TODO: The TestSuccessfulException thrown in @AfterAll is swallowed when run from the CLI.
9899
allowed_findings = ["java.io.IOException"],
100+
expected_warning_or_error = "ERROR: com.example.TestSuccessfulException: Lifecycle methods invoked as expected",
99101
fuzzer_args = [
100102
"-runs=3",
101103
],
@@ -117,11 +119,13 @@ java_fuzz_target_test(
117119
java_fuzz_target_test(
118120
name = "PerTestLifecycleFuzzTest",
119121
srcs = ["PerTestLifecycleFuzzTest.java"],
120-
allowed_findings = ["com.example.TestSuccessfulException"],
122+
expect_crash = True,
123+
expected_warning_or_error = "ERROR: com.example.TestSuccessfulException: Lifecycle methods invoked as expected",
121124
fuzzer_args = [
122125
"-runs=3",
123126
],
124127
target_class = "com.example.PerTestLifecycleFuzzTest",
128+
verify_crash_input = False,
125129
verify_crash_reproducer = False,
126130
runtime_deps = [
127131
":junit_runtime",

sanitizers/src/test/java/com/example/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ java_fuzz_target_test(
4848
"ExpressionLanguageInjection.java",
4949
],
5050
allowed_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh"],
51+
expected_warning_or_error = "WARN: Some hooks could not be applied to class files built for Java 7 or lower.",
5152
target_class = "com.example.ExpressionLanguageInjection",
5253
# The reproducer can't find jaz.Zer and thus doesn't crash.
5354
verify_crash_reproducer = False,

src/main/java/com/code_intelligence/jazzer/api/BugDetectors.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ private static AtomicReference<BiPredicate<String, Integer>> getConnectionPermit
9999
return (AtomicReference<BiPredicate<String, Integer>>)
100100
ssrfSanitizer.getField("connectionPermitted").get(null);
101101
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
102-
System.err.println("WARNING: ");
102+
System.err.println("WARN: ");
103103
e.printStackTrace();
104104
return null;
105105
}

tests/BUILD.bazel

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ java_fuzz_target_test(
205205
name = "NoCoverageFuzzer",
206206
timeout = "short",
207207
srcs = ["src/test/java/com/example/NoCoverageFuzzer.java"],
208+
expected_warning_or_error = "WARNING: no interesting inputs were found so far. Is the code instrumented for coverage?",
208209
fuzzer_args = [
209210
"-runs=10",
210211
"--instrumentation_excludes=**",
@@ -485,6 +486,11 @@ java_fuzz_target_test(
485486
timeout = "short",
486487
srcs = ["src/test/java/com/example/OfflineInstrumentedFuzzer.java"],
487488
allowed_findings = ["java.lang.IllegalStateException"],
489+
fuzzer_args = [
490+
# Do not instrument the JaCoCo agent.
491+
"--instrumentation_includes=com.example.*",
492+
"--custom_hook_includes=com.example.*",
493+
],
488494
target_class = "com.example.OfflineInstrumentedFuzzer",
489495
deps = [
490496
":OfflineInstrumentedTargetInstrumented",

0 commit comments

Comments
 (0)