1313// limitations under the License.
1414package com .code_intelligence .jazzer .tools ;
1515
16+ import static java .util .Arrays .asList ;
17+ import static java .util .Collections .unmodifiableSet ;
1618import static java .util .stream .Collectors .toList ;
19+ import static java .util .stream .Collectors .toSet ;
1720
1821import com .google .devtools .build .runfiles .AutoBazelRepository ;
1922import com .google .devtools .build .runfiles .Runfiles ;
3538import java .util .Collections ;
3639import java .util .Comparator ;
3740import java .util .List ;
41+ import java .util .Optional ;
3842import java .util .Set ;
3943import java .util .regex .Pattern ;
4044import java .util .stream .Collectors ;
4751
4852@ AutoBazelRepository
4953public 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 = "\t at " ;
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 );
0 commit comments