1 // Copyright 2021 Code Intelligence GmbH 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.code_intelligence.jazzer.replay; 16 17 import com.code_intelligence.jazzer.api.FuzzedDataProvider; 18 import com.code_intelligence.jazzer.driver.FuzzedDataProviderImpl; 19 import java.io.IOException; 20 import java.lang.reflect.InvocationTargetException; 21 import java.lang.reflect.Method; 22 import java.nio.file.Files; 23 import java.nio.file.Paths; 24 import java.util.Arrays; 25 26 public class Replayer { 27 public static final int STATUS_FINDING = 77; 28 public static final int STATUS_OTHER_ERROR = 1; 29 main(String[] args)30 public static void main(String[] args) { 31 if (args.length < 2) { 32 System.err.println("Usage: <fuzz target class> <input file path> <fuzzerInitialize args>..."); 33 System.exit(STATUS_OTHER_ERROR); 34 } 35 ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true); 36 37 Class<?> fuzzTargetClass; 38 try { 39 fuzzTargetClass = Class.forName(args[0]); 40 } catch (ClassNotFoundException e) { 41 e.printStackTrace(); 42 System.exit(STATUS_OTHER_ERROR); 43 // Without this return the compiler sees fuzzTargetClass as possibly uninitialized. 44 return; 45 } 46 47 String inputFilePath = args[1]; 48 byte[] input = loadInput(inputFilePath); 49 50 String[] fuzzTargetArgs = Arrays.copyOfRange(args, 2, args.length); 51 executeFuzzerInitialize(fuzzTargetClass, fuzzTargetArgs); 52 executeFuzzTarget(fuzzTargetClass, input); 53 } 54 loadInput(String inputFilePath)55 private static byte[] loadInput(String inputFilePath) { 56 try { 57 return Files.readAllBytes(Paths.get(inputFilePath)); 58 } catch (IOException e) { 59 e.printStackTrace(); 60 System.exit(STATUS_OTHER_ERROR); 61 // Without this return the compiler sees loadInput as possibly not returning a value. 62 return null; 63 } 64 } 65 executeFuzzerInitialize(Class<?> fuzzTarget, String[] args)66 private static void executeFuzzerInitialize(Class<?> fuzzTarget, String[] args) { 67 // public static void fuzzerInitialize() 68 try { 69 Method fuzzerInitialize = fuzzTarget.getMethod("fuzzerInitialize"); 70 fuzzerInitialize.invoke(null); 71 return; 72 } catch (Exception e) { 73 handleInvokeException(e, fuzzTarget); 74 } 75 // public static void fuzzerInitialize(String[] args) 76 try { 77 Method fuzzerInitialize = fuzzTarget.getMethod("fuzzerInitialize", String[].class); 78 fuzzerInitialize.invoke(null, (Object) args); 79 } catch (Exception e) { 80 handleInvokeException(e, fuzzTarget); 81 } 82 } 83 executeFuzzTarget(Class<?> fuzzTarget, byte[] input)84 public static void executeFuzzTarget(Class<?> fuzzTarget, byte[] input) { 85 // public static void fuzzerTestOneInput(byte[] input) 86 try { 87 Method fuzzerTestOneInput = fuzzTarget.getMethod("fuzzerTestOneInput", byte[].class); 88 fuzzerTestOneInput.invoke(null, (Object) input); 89 return; 90 } catch (Exception e) { 91 handleInvokeException(e, fuzzTarget); 92 } 93 // public static void fuzzerTestOneInput(FuzzedDataProvider data) 94 try { 95 Method fuzzerTestOneInput = 96 fuzzTarget.getMethod("fuzzerTestOneInput", FuzzedDataProvider.class); 97 try (FuzzedDataProviderImpl fuzzedDataProvider = FuzzedDataProviderImpl.withJavaData(input)) { 98 fuzzerTestOneInput.invoke(null, fuzzedDataProvider); 99 } 100 return; 101 } catch (Exception e) { 102 handleInvokeException(e, fuzzTarget); 103 } 104 System.err.printf("%s must define exactly one of the following two functions:%n" 105 + " public static void fuzzerTestOneInput(byte[] ...)%n" 106 + " public static void fuzzerTestOneInput(FuzzedDataProvider ...)%n" 107 + "Note: Fuzz targets returning boolean are no longer supported; exceptions should%n" 108 + "be thrown instead of returning true.%n", 109 fuzzTarget.getName()); 110 System.exit(STATUS_OTHER_ERROR); 111 } 112 handleInvokeException(Exception e, Class<?> fuzzTarget)113 private static void handleInvokeException(Exception e, Class<?> fuzzTarget) { 114 if (e instanceof NoSuchMethodException) 115 return; 116 if (e instanceof InvocationTargetException) { 117 filterOutOwnStackTraceElements(e.getCause(), fuzzTarget); 118 e.getCause().printStackTrace(); 119 System.exit(STATUS_FINDING); 120 } else { 121 e.printStackTrace(); 122 System.exit(STATUS_OTHER_ERROR); 123 } 124 } 125 filterOutOwnStackTraceElements(Throwable t, Class<?> fuzzTarget)126 private static void filterOutOwnStackTraceElements(Throwable t, Class<?> fuzzTarget) { 127 if (t.getCause() != null) 128 filterOutOwnStackTraceElements(t.getCause(), fuzzTarget); 129 if (t.getStackTrace() == null || t.getStackTrace().length == 0) 130 return; 131 StackTraceElement lowestFrame = t.getStackTrace()[t.getStackTrace().length - 1]; 132 if (!lowestFrame.getClassName().equals(Replayer.class.getName()) 133 || !lowestFrame.getMethodName().equals("main")) 134 return; 135 for (int i = t.getStackTrace().length - 1; i >= 0; i--) { 136 StackTraceElement frame = t.getStackTrace()[i]; 137 if (frame.getClassName().equals(fuzzTarget.getName()) 138 && frame.getMethodName().equals("fuzzerTestOneInput")) { 139 t.setStackTrace(Arrays.copyOfRange(t.getStackTrace(), 0, i + 1)); 140 break; 141 } 142 } 143 } 144 } 145