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 static com.code_intelligence.jazzer.runtime.Constants.IS_ANDROID; 18 19 import com.code_intelligence.jazzer.utils.UnsafeProvider; 20 import com.github.fmeum.rules_jni.RulesJni; 21 import java.lang.invoke.MethodHandle; 22 import java.lang.invoke.MethodHandles; 23 import java.lang.invoke.MethodType; 24 import java.util.Collections; 25 import java.util.HashSet; 26 import java.util.Set; 27 import sun.misc.Unsafe; 28 29 /** 30 * Represents the Java view on a libFuzzer 8 bit counter coverage map. By using a direct ByteBuffer, 31 * the counters are shared directly with native code. 32 */ 33 final public class CoverageMap { 34 static { 35 RulesJni.loadLibrary("jazzer_driver", "/com/code_intelligence/jazzer/driver"); 36 } 37 38 private static final String ENV_MAX_NUM_COUNTERS = "JAZZER_MAX_NUM_COUNTERS"; 39 40 private static final int MAX_NUM_COUNTERS = System.getenv(ENV_MAX_NUM_COUNTERS) != null 41 ? Integer.parseInt(System.getenv(ENV_MAX_NUM_COUNTERS)) 42 : 1 << 20; 43 44 private static final Unsafe UNSAFE = UnsafeProvider.getUnsafe(); 45 private static final Class<?> LOG; 46 private static final MethodHandle LOG_INFO; 47 private static final MethodHandle LOG_ERROR; 48 49 static { 50 try { 51 LOG = Class.forName( 52 "com.code_intelligence.jazzer.utils.Log", false, ClassLoader.getSystemClassLoader()); 53 LOG_INFO = MethodHandles.lookup().findStatic( 54 LOG, "info", MethodType.methodType(void.class, String.class)); 55 LOG_ERROR = MethodHandles.lookup().findStatic( 56 LOG, "error", MethodType.methodType(void.class, String.class, Throwable.class)); 57 } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { 58 throw new RuntimeException(e); 59 } 60 } 61 62 /** 63 * The collection of coverage counters directly interacted with by classes that are instrumented 64 * for coverage. The instrumentation assumes that this is always one contiguous block of memory, 65 * so it is allocated once at maximum size. Using a larger number here increases the memory usage 66 * of all fuzz targets, but has otherwise no impact on performance. 67 */ 68 public static final long countersAddress = UNSAFE.allocateMemory(MAX_NUM_COUNTERS); 69 70 static { UNSAFE.setMemory(countersAddress, MAX_NUM_COUNTERS, (byte) 0)71 UNSAFE.setMemory(countersAddress, MAX_NUM_COUNTERS, (byte) 0); 72 initialize(countersAddress); 73 } 74 75 private static final int INITIAL_NUM_COUNTERS = 1 << 9; 76 77 static { 78 registerNewCounters(0, INITIAL_NUM_COUNTERS); 79 } 80 81 /** 82 * The number of coverage counters that are currently registered with libFuzzer. This number grows 83 * dynamically as classes are instrumented and should be kept as low as possible as libFuzzer has 84 * to iterate over the whole map for every execution. 85 */ 86 private static int currentNumCounters = INITIAL_NUM_COUNTERS; 87 88 // Called via reflection. 89 @SuppressWarnings("unused") enlargeIfNeeded(int nextId)90 public static void enlargeIfNeeded(int nextId) { 91 int newNumCounters = currentNumCounters; 92 while (nextId >= newNumCounters) { 93 newNumCounters = 2 * newNumCounters; 94 if (newNumCounters > MAX_NUM_COUNTERS) { 95 logError( 96 String.format( 97 "Maximum number (%s) of coverage counters exceeded. Try to limit the scope of a single fuzz target as " 98 + "much as possible to keep the fuzzer fast. If that is not possible, the maximum number of " 99 + "counters can be increased via the %s environment variable.", 100 MAX_NUM_COUNTERS, ENV_MAX_NUM_COUNTERS), 101 null); 102 System.exit(1); 103 } 104 } 105 if (newNumCounters > currentNumCounters) { 106 registerNewCounters(currentNumCounters, newNumCounters); 107 currentNumCounters = newNumCounters; 108 logInfo("New number of coverage counters: " + currentNumCounters); 109 } 110 } 111 112 // Called by the coverage instrumentation. 113 @SuppressWarnings("unused") recordCoverage(final int id)114 public static void recordCoverage(final int id) { 115 if (IS_ANDROID) { 116 enlargeIfNeeded(id); 117 } 118 119 final long address = countersAddress + id; 120 final byte counter = UNSAFE.getByte(address); 121 UNSAFE.putByte(address, (byte) (counter == -1 ? 1 : counter + 1)); 122 } 123 getCoveredIds()124 public static Set<Integer> getCoveredIds() { 125 Set<Integer> coveredIds = new HashSet<>(); 126 for (int id = 0; id < currentNumCounters; id++) { 127 if (UNSAFE.getByte(countersAddress + id) > 0) { 128 coveredIds.add(id); 129 } 130 } 131 return Collections.unmodifiableSet(coveredIds); 132 } 133 replayCoveredIds(Set<Integer> coveredIds)134 public static void replayCoveredIds(Set<Integer> coveredIds) { 135 for (int id : coveredIds) { 136 UNSAFE.putByte(countersAddress + id, (byte) 1); 137 } 138 } 139 logInfo(String message)140 private static void logInfo(String message) { 141 try { 142 LOG_INFO.invokeExact(message); 143 } catch (Throwable error) { 144 // Should not be reached, Log.error does not throw. 145 error.printStackTrace(); 146 System.err.println("Failed to call Log.info:"); 147 System.err.println(message); 148 } 149 } 150 logError(String message, Throwable t)151 private static void logError(String message, Throwable t) { 152 try { 153 LOG_ERROR.invokeExact(message, t); 154 } catch (Throwable error) { 155 // Should not be reached, Log.error does not throw. 156 error.printStackTrace(); 157 System.err.println("Failed to call Log.error:"); 158 System.err.println(message); 159 } 160 } 161 162 // Returns the IDs of all blocks that have been covered in at least one run (not just the current 163 // one). getEverCoveredIds()164 public static native int[] getEverCoveredIds(); 165 initialize(long countersAddress)166 private static native void initialize(long countersAddress); 167 registerNewCounters(int oldNumCounters, int newNumCounters)168 private static native void registerNewCounters(int oldNumCounters, int newNumCounters); 169 } 170