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.runtime; 16 17 import com.code_intelligence.jazzer.utils.Utils; 18 import com.github.fmeum.rules_jni.RulesJni; 19 import java.lang.reflect.Executable; 20 import java.nio.charset.Charset; 21 22 @SuppressWarnings("unused") 23 final public class TraceDataFlowNativeCallbacks { 24 static { 25 RulesJni.loadLibrary("jazzer_driver", "/com/code_intelligence/jazzer/driver"); 26 } 27 28 // Note that we are not encoding as modified UTF-8 here: The FuzzedDataProvider transparently 29 // converts CESU8 into modified UTF-8 by coding null bytes on two bytes. Since the fuzzer is more 30 // likely to insert literal null bytes, having both the fuzzer input and the reported string 31 // comparisons be CESU8 should perform even better than the current implementation using modified 32 // UTF-8. 33 private static final Charset FUZZED_DATA_CHARSET = Charset.forName("CESU8"); 34 traceMemcmp(byte[] b1, byte[] b2, int result, int pc)35 public static native void traceMemcmp(byte[] b1, byte[] b2, int result, int pc); 36 traceStrcmp(String s1, String s2, int result, int pc)37 public static void traceStrcmp(String s1, String s2, int result, int pc) { 38 traceMemcmp(encodeForLibFuzzer(s1), encodeForLibFuzzer(s2), result, pc); 39 } 40 traceStrstr(String s1, String s2, int pc)41 public static void traceStrstr(String s1, String s2, int pc) { 42 traceStrstr0(encodeForLibFuzzer(s2), pc); 43 } 44 traceReflectiveCall(Executable callee, int pc)45 public static void traceReflectiveCall(Executable callee, int pc) { 46 String className = callee.getDeclaringClass().getCanonicalName(); 47 String executableName = callee.getName(); 48 String descriptor = Utils.getDescriptor(callee); 49 tracePcIndir(Utils.simpleFastHash(className, executableName, descriptor), pc); 50 } 51 traceCmpLongWrapper(long arg1, long arg2, int pc)52 public static int traceCmpLongWrapper(long arg1, long arg2, int pc) { 53 traceCmpLong(arg1, arg2, pc); 54 // Long.compare serves as a substitute for the lcmp opcode, which can't be used directly 55 // as the stack layout required for the call can't be achieved without local variables. 56 return Long.compare(arg1, arg2); 57 } 58 59 // The caller has to ensure that arg1 and arg2 have the same class. traceGenericCmp(Object arg1, Object arg2, int pc)60 public static void traceGenericCmp(Object arg1, Object arg2, int pc) { 61 if (arg1 instanceof CharSequence) { 62 traceStrcmp(arg1.toString(), arg2.toString(), 1, pc); 63 } else if (arg1 instanceof Integer) { 64 traceCmpInt((int) arg1, (int) arg2, pc); 65 } else if (arg1 instanceof Long) { 66 traceCmpLong((long) arg1, (long) arg2, pc); 67 } else if (arg1 instanceof Short) { 68 traceCmpInt((short) arg1, (short) arg2, pc); 69 } else if (arg1 instanceof Byte) { 70 traceCmpInt((byte) arg1, (byte) arg2, pc); 71 } else if (arg1 instanceof Character) { 72 traceCmpInt((char) arg1, (char) arg2, pc); 73 } else if (arg1 instanceof Number) { 74 traceCmpLong(((Number) arg1).longValue(), ((Number) arg2).longValue(), pc); 75 } else if (arg1 instanceof byte[]) { 76 traceMemcmp((byte[]) arg1, (byte[]) arg2, 1, pc); 77 } 78 } 79 80 /* trace-cmp */ traceCmpInt(int arg1, int arg2, int pc)81 public static native void traceCmpInt(int arg1, int arg2, int pc); traceConstCmpInt(int arg1, int arg2, int pc)82 public static native void traceConstCmpInt(int arg1, int arg2, int pc); traceCmpLong(long arg1, long arg2, int pc)83 public static native void traceCmpLong(long arg1, long arg2, int pc); traceSwitch(long val, long[] cases, int pc)84 public static native void traceSwitch(long val, long[] cases, int pc); 85 /* trace-div */ traceDivInt(int val, int pc)86 public static native void traceDivInt(int val, int pc); traceDivLong(long val, int pc)87 public static native void traceDivLong(long val, int pc); 88 /* trace-gep */ traceGep(long val, int pc)89 public static native void traceGep(long val, int pc); 90 /* indirect-calls */ tracePcIndir(int callee, int caller)91 public static native void tracePcIndir(int callee, int caller); 92 handleLibraryLoad()93 public static native void handleLibraryLoad(); 94 encodeForLibFuzzer(String str)95 private static byte[] encodeForLibFuzzer(String str) { 96 // libFuzzer string hooks only ever consume the first 64 bytes, so we can definitely cut the 97 // string off after 64 characters. 98 return str.substring(0, Math.min(str.length(), 64)).getBytes(FUZZED_DATA_CHARSET); 99 } 100 traceStrstr0(byte[] needle, int pc)101 private static native void traceStrstr0(byte[] needle, int pc); 102 } 103