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