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.driver; 16 17 import com.code_intelligence.jazzer.api.FuzzedDataProvider; 18 import com.code_intelligence.jazzer.utils.UnsafeProvider; 19 import com.github.fmeum.rules_jni.RulesJni; 20 import sun.misc.Unsafe; 21 22 public class FuzzedDataProviderImpl implements FuzzedDataProvider, AutoCloseable { 23 static { 24 RulesJni.loadLibrary("jazzer_fuzzed_data_provider", "/com/code_intelligence/jazzer/driver"); nativeInit()25 nativeInit(); 26 } 27 nativeInit()28 private static native void nativeInit(); 29 30 private final byte[] javaData; 31 private long originalDataPtr; 32 private int originalRemainingBytes; 33 34 // Accessed in fuzzed_data_provider.cpp. 35 private long dataPtr; 36 private int remainingBytes; 37 FuzzedDataProviderImpl(long dataPtr, int remainingBytes, byte[] javaData)38 private FuzzedDataProviderImpl(long dataPtr, int remainingBytes, byte[] javaData) { 39 this.javaData = javaData; 40 this.originalDataPtr = dataPtr; 41 this.dataPtr = dataPtr; 42 this.originalRemainingBytes = remainingBytes; 43 this.remainingBytes = remainingBytes; 44 } 45 46 /** 47 * Creates a {@link FuzzedDataProvider} that consumes bytes from an already existing native array. 48 * 49 * <ul> 50 * <li>{@link #close()} <b>must</b> be called on instances created with this method to free the 51 * native copy of the Java 52 * {@code byte} array. 53 * <li>{@link #setNativeData(long, int)} <b>must not</b> be called on instances created with this 54 * method. 55 * 56 * @param data the raw bytes used as input 57 * @return a {@link FuzzedDataProvider} backed by {@code data} 58 */ withJavaData(byte[] data)59 public static FuzzedDataProviderImpl withJavaData(byte[] data) { 60 return new FuzzedDataProviderImpl(allocateNativeCopy(data), data.length, data); 61 } 62 63 /** 64 * Creates a {@link FuzzedDataProvider} that consumes bytes from an already existing native array. 65 * 66 * <p>The backing array can be set at any time using {@link #setNativeData(long, int)} and is 67 * initially empty. 68 * 69 * @return a {@link FuzzedDataProvider} backed by an empty array. 70 */ withNativeData()71 public static FuzzedDataProviderImpl withNativeData() { 72 return new FuzzedDataProviderImpl(0, 0, null); 73 } 74 75 /** 76 * Replaces the current native backing array. 77 * 78 * <p><b>Must not</b> be called on instances created with {@link #withJavaData(byte[])}. 79 * 80 * @param dataPtr a native pointer to the new backing array 81 * @param dataLength the length of the new backing array 82 */ setNativeData(long dataPtr, int dataLength)83 public void setNativeData(long dataPtr, int dataLength) { 84 this.originalDataPtr = dataPtr; 85 this.dataPtr = dataPtr; 86 this.originalRemainingBytes = dataLength; 87 this.remainingBytes = dataLength; 88 } 89 90 /** 91 * Returns the Java byte array used to construct the instance, or null if it was created with 92 * {@link FuzzedDataProviderImpl#withNativeData()}; 93 */ getJavaData()94 public byte[] getJavaData() { 95 return javaData; 96 } 97 98 /** 99 * Resets the FuzzedDataProvider state to read from the beginning to the end of its current 100 * backing item. 101 */ reset()102 public void reset() { 103 dataPtr = originalDataPtr; 104 remainingBytes = originalRemainingBytes; 105 } 106 107 /** 108 * Releases native memory allocated for this instance (if any). 109 * 110 * <p>While the instance should not be used after this method returns, no usage of {@link 111 * FuzzedDataProvider} methods can result in memory corruption. 112 */ 113 @Override close()114 public void close() { 115 if (originalDataPtr == 0) { 116 return; 117 } 118 // We own the native memory iff the instance was created backed by a Java byte array. 119 if (javaData != null) { 120 UNSAFE.freeMemory(originalDataPtr); 121 } 122 // Prevent double-frees and use-after-frees by effectively making all methods no-ops after 123 // close() has been called. 124 originalDataPtr = 0; 125 originalRemainingBytes = 0; 126 dataPtr = 0; 127 remainingBytes = 0; 128 } 129 130 private static final Unsafe UNSAFE = UnsafeProvider.getUnsafe(); 131 private static final long BYTE_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); 132 allocateNativeCopy(byte[] data)133 private static long allocateNativeCopy(byte[] data) { 134 long nativeCopy = UNSAFE.allocateMemory(data.length); 135 UNSAFE.copyMemory(data, BYTE_ARRAY_OFFSET, null, nativeCopy, data.length); 136 return nativeCopy; 137 } 138 consumeBoolean()139 @Override public native boolean consumeBoolean(); 140 consumeBooleans(int maxLength)141 @Override public native boolean[] consumeBooleans(int maxLength); 142 consumeByte()143 @Override public native byte consumeByte(); 144 145 @Override consumeByte(byte min, byte max)146 public byte consumeByte(byte min, byte max) { 147 if (min > max) { 148 throw new IllegalArgumentException( 149 String.format("min must be <= max (got min: %d, max: %d)", min, max)); 150 } 151 return consumeByteUnchecked(min, max); 152 } 153 consumeShort()154 @Override public native short consumeShort(); 155 156 @Override consumeShort(short min, short max)157 public short consumeShort(short min, short max) { 158 if (min > max) { 159 throw new IllegalArgumentException( 160 String.format("min must be <= max (got min: %d, max: %d)", min, max)); 161 } 162 return consumeShortUnchecked(min, max); 163 } 164 consumeShorts(int maxLength)165 @Override public native short[] consumeShorts(int maxLength); 166 consumeInt()167 @Override public native int consumeInt(); 168 169 @Override consumeInt(int min, int max)170 public int consumeInt(int min, int max) { 171 if (min > max) { 172 throw new IllegalArgumentException( 173 String.format("min must be <= max (got min: %d, max: %d)", min, max)); 174 } 175 return consumeIntUnchecked(min, max); 176 } 177 consumeInts(int maxLength)178 @Override public native int[] consumeInts(int maxLength); 179 consumeLong()180 @Override public native long consumeLong(); 181 182 @Override consumeLong(long min, long max)183 public long consumeLong(long min, long max) { 184 if (min > max) { 185 throw new IllegalArgumentException( 186 String.format("min must be <= max (got min: %d, max: %d)", min, max)); 187 } 188 return consumeLongUnchecked(min, max); 189 } 190 consumeLongs(int maxLength)191 @Override public native long[] consumeLongs(int maxLength); 192 consumeFloat()193 @Override public native float consumeFloat(); 194 consumeRegularFloat()195 @Override public native float consumeRegularFloat(); 196 197 @Override consumeRegularFloat(float min, float max)198 public float consumeRegularFloat(float min, float max) { 199 if (min > max) { 200 throw new IllegalArgumentException( 201 String.format("min must be <= max (got min: %f, max: %f)", min, max)); 202 } 203 return consumeRegularFloatUnchecked(min, max); 204 } 205 consumeProbabilityFloat()206 @Override public native float consumeProbabilityFloat(); 207 consumeDouble()208 @Override public native double consumeDouble(); 209 210 @Override consumeRegularDouble(double min, double max)211 public double consumeRegularDouble(double min, double max) { 212 if (min > max) { 213 throw new IllegalArgumentException( 214 String.format("min must be <= max (got min: %f, max: %f)", min, max)); 215 } 216 return consumeRegularDoubleUnchecked(min, max); 217 } 218 consumeRegularDouble()219 @Override public native double consumeRegularDouble(); 220 consumeProbabilityDouble()221 @Override public native double consumeProbabilityDouble(); 222 consumeChar()223 @Override public native char consumeChar(); 224 225 @Override consumeChar(char min, char max)226 public char consumeChar(char min, char max) { 227 if (min > max) { 228 throw new IllegalArgumentException( 229 String.format("min must be <= max (got min: %c, max: %c)", min, max)); 230 } 231 return consumeCharUnchecked(min, max); 232 } 233 consumeCharNoSurrogates()234 @Override public native char consumeCharNoSurrogates(); 235 consumeAsciiString(int maxLength)236 @Override public native String consumeAsciiString(int maxLength); 237 consumeString(int maxLength)238 @Override public native String consumeString(int maxLength); 239 consumeRemainingAsAsciiString()240 @Override public native String consumeRemainingAsAsciiString(); 241 consumeRemainingAsString()242 @Override public native String consumeRemainingAsString(); 243 consumeBytes(int maxLength)244 @Override public native byte[] consumeBytes(int maxLength); 245 consumeRemainingAsBytes()246 @Override public native byte[] consumeRemainingAsBytes(); 247 remainingBytes()248 @Override public native int remainingBytes(); 249 consumeByteUnchecked(byte min, byte max)250 private native byte consumeByteUnchecked(byte min, byte max); consumeShortUnchecked(short min, short max)251 private native short consumeShortUnchecked(short min, short max); consumeCharUnchecked(char min, char max)252 private native char consumeCharUnchecked(char min, char max); consumeIntUnchecked(int min, int max)253 private native int consumeIntUnchecked(int min, int max); consumeLongUnchecked(long min, long max)254 private native long consumeLongUnchecked(long min, long max); consumeRegularFloatUnchecked(float min, float max)255 private native float consumeRegularFloatUnchecked(float min, float max); consumeRegularDoubleUnchecked(double min, double max)256 private native double consumeRegularDoubleUnchecked(double min, double max); 257 } 258