From 6b829d4e50188e4b0da46c96174ccb4f997bb4be Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Thu, 3 Jul 2025 17:42:00 +0200 Subject: [PATCH 1/9] Add `Unsafe` array access sanitizer --- sanitizers/sanitizers.bzl | 1 + .../jazzer/sanitizers/BUILD.bazel | 10 + .../sanitizers/UnsafeArrayOutOfBounds.java | 457 +++++++++++++++ .../src/test/java/com/example/BUILD.bazel | 25 + .../com/example/UnsafeArrayOutOfBounds.java | 552 ++++++++++++++++++ .../example/UnsafeArrayOutOfBoundsValid.java | 272 +++++++++ 6 files changed, 1317 insertions(+) create mode 100644 sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java create mode 100644 sanitizers/src/test/java/com/example/UnsafeArrayOutOfBounds.java create mode 100644 sanitizers/src/test/java/com/example/UnsafeArrayOutOfBoundsValid.java diff --git a/sanitizers/sanitizers.bzl b/sanitizers/sanitizers.bzl index ffa30114a..3b17c1bb2 100644 --- a/sanitizers/sanitizers.bzl +++ b/sanitizers/sanitizers.bzl @@ -31,6 +31,7 @@ _sanitizer_class_names = [ "ScriptEngineInjection", "ServerSideRequestForgery", "SqlInjection", + "UnsafeArrayOutOfBounds", "XPathInjection", ] diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel index 60d2e02e9..2de8432c8 100644 --- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel @@ -48,6 +48,15 @@ java_library( ], ) +java_library( + name = "unsafe_array_out_of_bounds", + srcs = ["UnsafeArrayOutOfBounds.java"], + deps = [ + "//src/main/java/com/code_intelligence/jazzer/api:hooks", + "//src/main/java/com/code_intelligence/jazzer/utils:unsafe_provider", + ], +) + kt_jvm_library( name = "sanitizers", srcs = [ @@ -69,6 +78,7 @@ kt_jvm_library( ":script_engine_injection", ":server_side_request_forgery", ":sql_injection", + ":unsafe_array_out_of_bounds", ], deps = [ "//src/main/java/com/code_intelligence/jazzer/api:hooks", diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java new file mode 100644 index 000000000..912063312 --- /dev/null +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java @@ -0,0 +1,457 @@ +/* + * Copyright 2025 Code Intelligence GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.code_intelligence.jazzer.sanitizers; + +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueCritical; +import com.code_intelligence.jazzer.api.HookType; +import com.code_intelligence.jazzer.api.Jazzer; +import com.code_intelligence.jazzer.api.MethodHook; +import com.code_intelligence.jazzer.utils.UnsafeProvider; +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Array; +import sun.jvm.hotspot.utilities.AssertionFailure; +import sun.misc.Unsafe; + +/** + * Sanitizer for {@link Unsafe sun.misc.Unsafe} usage which performs out-of-bounds and some other + * invalid access on arrays. + */ +public class UnsafeArrayOutOfBounds { + /* + * Implementation notes: + * - This only covers the 'public' class sun.misc.Unsafe, not the JDK-internal jdk.internal.misc.Unsafe since + * it is rather unlikely that user code uses that, especially with strong encapsulation of JDK internals (JEP 403) + * - This only covers Unsafe access for arrays: + * - Array access can be implemented in a stateless way because all information is available from the arguments + * passed to the Unsafe method + * - Sanitizing field access is probably not interesting because that is normally performed with hardcoded + * offsets, which cannot be influenced by fuzzing input data + * - Sanitizing native memory access would require keeping track of allocations, and is therefore due to its + * complexity out of scope for now + * - Alignment when reading a primitive value larger than 1 byte (e.g. an 8-byte long) from a primitive array of + * smaller element size is not checked + * It depends on the platform where the application is running whether unaligned access is supported, but many + * applications using Unsafe assume that unaligned access is supported. + * - Where necessary the hooks use `@MethodHook#targetMethodDescriptor` to select the Unsafe method overload + * with Object parameter, instead of the overload without Object parameter which only supports native memory access. + */ + + private static final Unsafe UNSAFE = UnsafeProvider.getUnsafe(); + private static final String UNSAFE_NAME = "sun.misc.Unsafe"; + + /* + Script for generating the @MethodHook annotations: + ==================================================== + Method[] methods = sun.misc.Unsafe.class.getMethods(); + Set allMethodNames = new HashSet<>(); + Set duplicateMethodNames = new HashSet<>(); + Arrays.stream(methods).map(Method::getName).forEach(n -> { + if (!allMethodNames.add(n)) { + duplicateMethodNames.add(n); + } + }); + + Arrays.stream(methods) + .filter(m -> Arrays.stream(m.getParameterTypes()).anyMatch(p -> p == Object.class)) + .filter(m -> !Set.of("equals", "unpark").contains(m.getName())) + .sorted(Comparator.comparing(Method::getName)) + .forEach(m -> { + String methodName = m.getName(); + String s = "@MethodHook(\n type = HookType.BEFORE,\n targetClassName = UNSAFE_NAME,\n targetMethod = \"" + + methodName + + "\""; + if (duplicateMethodNames.contains(methodName)) { + String methodDesc; + try { + methodDesc = MethodHandles.lookup().unreflect(m).type().toMethodDescriptorString(); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + s += ",\n targetMethodDescriptor = \"" + + methodDesc + + "\""; + } + System.out.println(s + ")"); + }); + ==================================================== + */ + + private static int getAccessSize(Class c) { + int accessSize; + if (c == boolean.class) { + // Assumes that `Unsafe.ARRAY_BOOLEAN_INDEX_SCALE > 0` + accessSize = 1; + } else if (c == byte.class) { + accessSize = 1; + } else if (c == char.class || c == short.class) { + accessSize = 2; + } else if (c == int.class || c == float.class) { + accessSize = 4; + } else if (c == long.class || c == double.class) { + accessSize = 8; + } else { + throw new AssertionFailure("Unexpected type: " + c); + } + return accessSize; + } + + /** Hook for all {@link Unsafe} methods which read or write an {@code Object} reference. */ + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "compareAndSwapObject") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getAndSetObject") + @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getObject") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getObjectVolatile") + @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "putObject") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putObjectVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putOrderedObject") + public static void objectAccessHook( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + checkObjectSizedAccess(arguments); + } + + /** + * Hook for all {@link Unsafe} {@code get...} primitive methods, where the access size can be + * derived from the return type. + */ + @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getAndAddInt") + @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getAndAddLong") + @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getAndSetInt") + @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getAndSetLong") + @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getBoolean") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getBooleanVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getByte", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)B") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getByteVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getChar", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)C") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getCharVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getDouble", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)D") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getDoubleVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getFloat", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)F") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getFloatVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getInt", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)I") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getIntVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getLong", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)J") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getLongVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getShort", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)S") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getShortVolatile") + public static void primitiveGetterHook( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + int accessSize = getAccessSize(method.type().returnType()); + checkAccess(arguments, accessSize); + } + + /** + * Hook for all {@link Unsafe} {@code compareAndSwap...} and {@code put...} primitive methods, + * where the access size can be derived from the parameter type at index 2 (0-based). + */ + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "compareAndSwapInt") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "compareAndSwapLong") + @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "putBoolean") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putBooleanVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putByte", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JB)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putByteVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putChar", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JC)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putCharVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putDouble", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JD)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putDoubleVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putFloat", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JF)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putFloatVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putInt", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JI)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putIntVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putLong", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JJ)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putLongVolatile") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putShort", + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JS)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putShortVolatile") + @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "putOrderedInt") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putOrderedLong") + public static void primitiveSetterHook( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + int accessSize = + getAccessSize(method.type().parameterType(2 + 1)); // + 1 for implicit Unsafe instance + checkAccess(arguments, accessSize); + } + + /** Hook for {@link Unsafe#setMemory(Object, long, long, byte)} */ + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "setMemory", + // Only handle overload with Object parameter + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JJB)V") + public static void setMemoryHook( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + checkAccess(arguments[0], (long) arguments[1], (long) arguments[2]); + } + + /** Hook for {@link Unsafe#copyMemory(Object, long, Object, long, long)} */ + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "copyMemory", + // Only handle overload with Object parameters + targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JLjava/lang/Object;JJ)V") + public static void copyMemoryHook( + MethodHandle method, Object thisObject, Object[] arguments, int hookId) { + long size = (long) arguments[4]; + checkAccess(arguments[0], (long) arguments[1], size); + checkAccess(arguments[2], (long) arguments[3], size); + } + + private static void report(String message) { + Jazzer.reportFindingFromHook(new FuzzerSecurityIssueCritical(message)); + } + + private static void checkAccess(Object[] args, long accessSize) { + Object obj = args[0]; + long offset = (long) args[1]; + checkAccess(obj, offset, accessSize); + } + + /** + * Checks {@link Unsafe} memory access where the access size is measured in number of bytes. + * + * @param obj the base object for memory access; e.g. for {@link Unsafe#getInt(Object, long)} it + * is the argument at index 0 + * @param offset the offset for the memory access; e.g. for {@link Unsafe#getInt(Object, long)} it + * is the argument at index 1 + * @param accessSize the number of bytes which is accessed; e.g. for {@link Unsafe#getInt(Object, + * long)} it is 4 (due to {@code int} being 4 bytes large) + * @see #checkObjectSizedAccess(Object, long) + */ + private static void checkAccess(Object obj, long offset, long accessSize) { + if (accessSize < 0) { + report("Negative access size: " + accessSize); + } + + if (obj == null) { + // Native memory access; not sanitized here + return; + } + + Class objClass = obj.getClass(); + Class componentType = objClass.getComponentType(); + if (componentType == null) { + // Not an array + return; + } + + if (!componentType.isPrimitive()) { + // Reading or writing bytes to an array of references; might be possible but seems + // rather unreliable and might mess with the garbage collector? + report("Reading or writing bytes from a " + objClass.getTypeName()); + } + + long baseOffset = UNSAFE.arrayBaseOffset(objClass); + long indexScale = UNSAFE.arrayIndexScale(objClass); + + if (offset < baseOffset) { + report("Offset " + offset + " lower than baseOffset " + baseOffset); + } + long endOffset = baseOffset + Array.getLength(obj) * indexScale; + // Uses `compareUnsigned` to account for overflow + if (Long.compareUnsigned(endOffset, offset + accessSize) < 0) { + report( + "Access at offset " + + offset + + " with size " + + accessSize + + " exceeds end offset " + + endOffset); + } + + // Don't check if access is aligned; depends on platform if unaligned access is supported + // and some libraries which are using Unsafe assume that it is supported + } + + private static void checkObjectSizedAccess(Object[] args) { + Object obj = args[0]; + long offset = (long) args[1]; + checkObjectSizedAccess(obj, offset); + } + + /** + * Checks {@link Unsafe} memory access where an object reference is accessed. + * + * @param obj the base object for memory access; e.g. for {@link Unsafe#getObject(Object, long)} + * it is the argument at index 0 + * @param offset the offset for the memory access; e.g. for {@link Unsafe#getObject(Object, long)} + * it is the argument at index 1 + * @see #checkAccess(Object, long, long) + */ + private static void checkObjectSizedAccess(Object obj, long offset) { + if (obj == null) { + // Native memory access; not sanitized here + return; + } + + Class objClass = obj.getClass(); + Class componentType = objClass.getComponentType(); + if (componentType == null) { + // Not an array + return; + } + + if (componentType.isPrimitive()) { + // Reading or writing object references from a primitive array; might be possible but seems + // rather unreliable and might mess with the garbage collector? + report("Reading or writing object reference from a " + objClass.getTypeName()); + } + + long baseOffset = UNSAFE.arrayBaseOffset(objClass); + long indexScale = UNSAFE.arrayIndexScale(objClass); + + if (offset < baseOffset) { + report("Offset " + offset + " lower than baseOffset " + baseOffset); + } + + if ((offset - baseOffset) % indexScale != 0) { + // Trying to read or write object at an offset which spans two array elements + report("Access at offset " + offset + " is not aligned"); + } + + long endOffset = baseOffset + Array.getLength(obj) * indexScale; + long accessSize = indexScale; + if (Long.compareUnsigned(endOffset, offset + accessSize) < 0) { + report("Access at offset " + offset + " exceeds end offset " + endOffset); + } + } +} diff --git a/sanitizers/src/test/java/com/example/BUILD.bazel b/sanitizers/src/test/java/com/example/BUILD.bazel index 695380c79..67b47831b 100644 --- a/sanitizers/src/test/java/com/example/BUILD.bazel +++ b/sanitizers/src/test/java/com/example/BUILD.bazel @@ -218,6 +218,31 @@ java_fuzz_target_test( ], ) +java_fuzz_target_test( + name = "UnsafeArrayOutOfBounds", + srcs = [ + "UnsafeArrayOutOfBounds.java", + ], + allowed_findings = [ + "com.code_intelligence.jazzer.api.FuzzerSecurityIssueCritical", + ], + target_class = "com.example.UnsafeArrayOutOfBounds", + verify_crash_reproducer = False, +) + +java_fuzz_target_test( + name = "UnsafeArrayOutOfBoundsValid", + srcs = [ + "UnsafeArrayOutOfBoundsValid.java", + ], + allowed_findings = [], + fuzzer_args = [ + # Test does not depend on fuzzing input; just run it once + "-runs=1", + ], + target_class = "com.example.UnsafeArrayOutOfBoundsValid", +) + java_fuzz_target_test( name = "ClojureTests", srcs = [ diff --git a/sanitizers/src/test/java/com/example/UnsafeArrayOutOfBounds.java b/sanitizers/src/test/java/com/example/UnsafeArrayOutOfBounds.java new file mode 100644 index 000000000..a33b74c89 --- /dev/null +++ b/sanitizers/src/test/java/com/example/UnsafeArrayOutOfBounds.java @@ -0,0 +1,552 @@ +/* + * Copyright 2025 Code Intelligence GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import sun.misc.Unsafe; + +public class UnsafeArrayOutOfBounds { + private static final Unsafe UNSAFE; + + static { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + UNSAFE = (Unsafe) f.get(null); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + /** Defines test methods which all perform invalid memory access with {@link Unsafe}. */ + @SuppressWarnings("unused") // test methods are accessed through reflection + private static class TestMethods { + static void compareAndSwapInt() { + UNSAFE.compareAndSwapInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 0, 1); + } + + static void compareAndSwapInt_end() { + UNSAFE.compareAndSwapInt( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 0, 1); + } + + static void compareAndSwapLong() { + UNSAFE.compareAndSwapLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 0, 1); + } + + static void compareAndSwapLong_end() { + UNSAFE.compareAndSwapLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0, 1); + } + + static void compareAndSwapObject() { + UNSAFE.compareAndSwapObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, "a", "b"); + } + + static void compareAndSwapObject_end() { + UNSAFE.compareAndSwapObject( + new Object[5], + Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, + "a", + "b"); + } + + static void getAndAddInt() { + UNSAFE.getAndAddInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 1); + } + + static void getAndAddInt_end() { + UNSAFE.getAndAddInt( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 1); + } + + static void getAndAddLong() { + UNSAFE.getAndAddLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 1); + } + + static void getAndAddLong_end() { + UNSAFE.getAndAddLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 1); + } + + static void getAndSetInt() { + UNSAFE.getAndSetInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 1); + } + + static void getAndSetInt_end() { + UNSAFE.getAndSetInt( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 1); + } + + static void getAndSetLong() { + UNSAFE.getAndSetLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 1); + } + + static void getAndSetLong_end() { + UNSAFE.getAndSetLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 1); + } + + static void getAndSetObject() { + UNSAFE.getAndSetObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, "a"); + } + + static void getAndSetObject_end() { + UNSAFE.getAndSetObject( + new Object[5], + Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, + "a"); + } + + static void getBoolean() { + UNSAFE.getBoolean(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET - 1); + } + + static void getBoolean_end() { + UNSAFE.getBoolean( + new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 5L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE); + } + + static void getBooleanVolatile() { + UNSAFE.getBooleanVolatile(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET - 1); + } + + static void getBooleanVolatile_end() { + UNSAFE.getBooleanVolatile( + new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 5L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE); + } + + static void getByte() { + UNSAFE.getByte(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1); + } + + static void getByte_end() { + UNSAFE.getByte( + new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET + 5L * Unsafe.ARRAY_BYTE_INDEX_SCALE); + } + + static void getByteVolatile() { + UNSAFE.getByteVolatile(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1); + } + + static void getByteVolatile_end() { + UNSAFE.getByteVolatile( + new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET + 5L * Unsafe.ARRAY_BYTE_INDEX_SCALE); + } + + static void getChar() { + UNSAFE.getChar(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET - 1); + } + + static void getChar_end() { + UNSAFE.getChar( + new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 5L * Unsafe.ARRAY_CHAR_INDEX_SCALE); + } + + static void getCharVolatile() { + UNSAFE.getCharVolatile(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET - 1); + } + + static void getCharVolatile_end() { + UNSAFE.getCharVolatile( + new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 5L * Unsafe.ARRAY_CHAR_INDEX_SCALE); + } + + static void getDouble() { + UNSAFE.getDouble(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET - 1); + } + + static void getDouble_end() { + UNSAFE.getDouble( + new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 5L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + } + + static void getDoubleVolatile() { + UNSAFE.getDoubleVolatile(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET - 1); + } + + static void getDoubleVolatile_end() { + UNSAFE.getDoubleVolatile( + new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 5L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + } + + static void getFloat() { + UNSAFE.getFloat(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET - 1); + } + + static void getFloat_end() { + UNSAFE.getFloat( + new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 5L * Unsafe.ARRAY_FLOAT_INDEX_SCALE); + } + + static void getFloatVolatile() { + UNSAFE.getFloatVolatile(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET - 1); + } + + static void getFloatVolatile_end() { + UNSAFE.getFloatVolatile( + new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 5L * Unsafe.ARRAY_FLOAT_INDEX_SCALE); + } + + static void getInt() { + UNSAFE.getInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1); + } + + static void getInt_end() { + UNSAFE.getInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE); + } + + static void getIntVolatile() { + UNSAFE.getIntVolatile(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1); + } + + static void getIntVolatile_end() { + UNSAFE.getIntVolatile( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE); + } + + static void getLong() { + UNSAFE.getLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1); + } + + static void getLong_end() { + UNSAFE.getLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE); + } + + static void getLongVolatile() { + UNSAFE.getLongVolatile(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1); + } + + static void getLongVolatile_end() { + UNSAFE.getLongVolatile( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE); + } + + static void getObject() { + UNSAFE.getObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1); + } + + static void getObject_end() { + UNSAFE.getObject( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE); + } + + static void getObjectVolatile() { + UNSAFE.getObjectVolatile(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1); + } + + static void getObjectVolatile_end() { + UNSAFE.getObjectVolatile( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE); + } + + static void getShort() { + UNSAFE.getShort(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET - 1); + } + + static void getShort_end() { + UNSAFE.getShort( + new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET + 5L * Unsafe.ARRAY_SHORT_INDEX_SCALE); + } + + static void getShortVolatile() { + UNSAFE.getShortVolatile(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET - 1); + } + + static void getShortVolatile_end() { + UNSAFE.getShortVolatile( + new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET + 5L * Unsafe.ARRAY_SHORT_INDEX_SCALE); + } + + static void putBoolean() { + UNSAFE.putBoolean(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET - 1, true); + } + + static void putBoolean_end() { + UNSAFE.putBoolean( + new boolean[5], + Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 5L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE, + true); + } + + static void putBooleanVolatile() { + UNSAFE.putBooleanVolatile(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET - 1, true); + } + + static void putBooleanVolatile_end() { + UNSAFE.putBooleanVolatile( + new boolean[5], + Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 5L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE, + true); + } + + static void putByte() { + UNSAFE.putByte(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, (byte) 0); + } + + static void putByte_end() { + UNSAFE.putByte( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET + 5L * Unsafe.ARRAY_BYTE_INDEX_SCALE, + (byte) 0); + } + + static void putByteVolatile() { + UNSAFE.putByteVolatile(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, (byte) 0); + } + + static void putByteVolatile_end() { + UNSAFE.putByteVolatile( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET + 5L * Unsafe.ARRAY_BYTE_INDEX_SCALE, + (byte) 0); + } + + static void putChar() { + UNSAFE.putChar(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET - 1, 'a'); + } + + static void putChar_end() { + UNSAFE.putChar( + new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 5L * Unsafe.ARRAY_CHAR_INDEX_SCALE, 'a'); + } + + static void putCharVolatile() { + UNSAFE.putCharVolatile(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET - 1, 'a'); + } + + static void putCharVolatile_end() { + UNSAFE.putCharVolatile( + new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 5L * Unsafe.ARRAY_CHAR_INDEX_SCALE, 'a'); + } + + static void putDouble() { + UNSAFE.putDouble(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET - 1, 0); + } + + static void putDouble_end() { + UNSAFE.putDouble( + new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 5L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, 0); + } + + static void putDoubleVolatile() { + UNSAFE.putDoubleVolatile(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET - 1, 0); + } + + static void putDoubleVolatile_end() { + UNSAFE.putDoubleVolatile( + new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 5L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, 0); + } + + static void putFloat() { + UNSAFE.putFloat(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET - 1, 0); + } + + static void putFloat_end() { + UNSAFE.putFloat( + new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 5L * Unsafe.ARRAY_FLOAT_INDEX_SCALE, 0); + } + + static void putFloatVolatile() { + UNSAFE.putFloatVolatile(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET - 1, 0); + } + + static void putFloatVolatile_end() { + UNSAFE.putFloatVolatile( + new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 5L * Unsafe.ARRAY_FLOAT_INDEX_SCALE, 0); + } + + static void putInt() { + UNSAFE.putInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 0); + } + + static void putInt_end() { + UNSAFE.putInt( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 0); + } + + static void putIntVolatile() { + UNSAFE.putIntVolatile(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 0); + } + + static void putIntVolatile_end() { + UNSAFE.putIntVolatile( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 0); + } + + static void putLong() { + UNSAFE.putLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 0); + } + + static void putLong_end() { + UNSAFE.putLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0); + } + + static void putLongVolatile() { + UNSAFE.putLongVolatile(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 0); + } + + static void putLongVolatile_end() { + UNSAFE.putLongVolatile( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0); + } + + static void putObject() { + UNSAFE.putObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, 0); + } + + static void putObject_end() { + UNSAFE.putObject( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, 0); + } + + static void putObjectVolatile() { + UNSAFE.putObjectVolatile(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, 0); + } + + static void putObjectVolatile_end() { + UNSAFE.putObjectVolatile( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, 0); + } + + static void putOrderedInt() { + UNSAFE.putOrderedInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 0); + } + + static void putOrderedInt_end() { + UNSAFE.putOrderedInt( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 0); + } + + static void putOrderedLong() { + UNSAFE.putOrderedLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 0); + } + + static void putOrderedLong_end() { + UNSAFE.putOrderedLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0); + } + + static void putOrderedObject() { + UNSAFE.putOrderedObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, 0); + } + + static void putOrderedObject_end() { + UNSAFE.putOrderedObject( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, 0); + } + + static void putShort() { + UNSAFE.putShort(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET - 1, (short) 0); + } + + static void putShort_end() { + UNSAFE.putShort( + new short[5], + Unsafe.ARRAY_SHORT_BASE_OFFSET + 5L * Unsafe.ARRAY_SHORT_INDEX_SCALE, + (short) 0); + } + + static void putShortVolatile() { + UNSAFE.putShortVolatile(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET - 1, (short) 0); + } + + static void putShortVolatile_end() { + UNSAFE.putShortVolatile( + new short[5], + Unsafe.ARRAY_SHORT_BASE_OFFSET + 5L * Unsafe.ARRAY_SHORT_INDEX_SCALE, + (short) 0); + } + + static void copyMemory() { + UNSAFE.copyMemory( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET, + 2); + } + + static void copyMemory_end() { + UNSAFE.copyMemory( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET + 4L * Unsafe.ARRAY_BYTE_INDEX_SCALE, + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET, + 2); + } + + static void copyMemory_dest() { + UNSAFE.copyMemory( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET, + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, + 2); + } + + static void copyMemory_dest_end() { + UNSAFE.copyMemory( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET, + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET + 4L * Unsafe.ARRAY_BYTE_INDEX_SCALE, + 2); + } + + static void setMemory() { + UNSAFE.setMemory(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, 2, (byte) 0); + } + + static void setMemory_end() { + UNSAFE.setMemory( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET + 4L * Unsafe.ARRAY_BYTE_INDEX_SCALE, + 2, + (byte) 0); + } + + // The following covers some additional special cases of invalid memory access + static void byteAccessOnObjectArray() { + UNSAFE.getByte(new String[10], Unsafe.ARRAY_OBJECT_BASE_OFFSET); + } + + static void objectAccessOnPrimitiveArray() { + UNSAFE.getObject(new byte[100], Unsafe.ARRAY_BYTE_BASE_OFFSET); + } + + static void unalignedObjectAccess() { + assert Unsafe.ARRAY_OBJECT_INDEX_SCALE != 1; + UNSAFE.getObject(new String[2], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 1); + } + } + + public static void fuzzerTestOneInput(FuzzedDataProvider data) throws Exception { + Method[] testMethods = TestMethods.class.getDeclaredMethods(); + // Since all of these methods are expected to cause a sanitizer exception, pick a random one and + // run it + // TODO: Is this a proper way to implement this? + Method testMethod = testMethods[data.consumeInt(0, testMethods.length - 1)]; + + testMethod.invoke(null); + + throw new AssertionError("No sanitizer exception was thrown for " + testMethod); + } +} diff --git a/sanitizers/src/test/java/com/example/UnsafeArrayOutOfBoundsValid.java b/sanitizers/src/test/java/com/example/UnsafeArrayOutOfBoundsValid.java new file mode 100644 index 000000000..62d937ba2 --- /dev/null +++ b/sanitizers/src/test/java/com/example/UnsafeArrayOutOfBoundsValid.java @@ -0,0 +1,272 @@ +/* + * Copyright 2025 Code Intelligence GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import java.lang.reflect.Field; +import sun.misc.Unsafe; + +/** Verifies that valid {@link Unsafe} usage does not cause a spurious sanitizer exception. */ +public class UnsafeArrayOutOfBoundsValid { + private static final Unsafe UNSAFE; + + static { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + UNSAFE = (Unsafe) f.get(null); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + public static void fuzzerTestOneInput(FuzzedDataProvider ignored) throws Exception { + UNSAFE.compareAndSwapInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET, 0, 1); + UNSAFE.compareAndSwapInt( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 4L * Unsafe.ARRAY_INT_INDEX_SCALE, 0, 1); + + UNSAFE.compareAndSwapLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET, 0, 1); + UNSAFE.compareAndSwapLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 4L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0, 1); + + UNSAFE.compareAndSwapObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET, "a", "b"); + UNSAFE.compareAndSwapObject( + new Object[5], + Unsafe.ARRAY_OBJECT_BASE_OFFSET + 4L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, + "a", + "b"); + + UNSAFE.getAndAddInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET, 1); + UNSAFE.getAndAddInt( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 4L * Unsafe.ARRAY_INT_INDEX_SCALE, 1); + + UNSAFE.getAndAddLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET, 1); + UNSAFE.getAndAddLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 4L * Unsafe.ARRAY_LONG_INDEX_SCALE, 1); + + UNSAFE.getAndSetInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET, 1); + UNSAFE.getAndSetInt( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 4L * Unsafe.ARRAY_INT_INDEX_SCALE, 1); + + UNSAFE.getAndSetLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET, 1); + UNSAFE.getAndSetLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 4L * Unsafe.ARRAY_LONG_INDEX_SCALE, 1); + + UNSAFE.getAndSetObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET, "a"); + UNSAFE.getAndSetObject( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 4L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, "a"); + + UNSAFE.getBoolean(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET); + UNSAFE.getBoolean( + new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 4L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE); + + UNSAFE.getBooleanVolatile(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET); + UNSAFE.getBooleanVolatile( + new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 4L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE); + + UNSAFE.getByte(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET); + UNSAFE.getByte(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET + 4L * Unsafe.ARRAY_BYTE_INDEX_SCALE); + + UNSAFE.getByteVolatile(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET); + UNSAFE.getByteVolatile( + new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET + 4L * Unsafe.ARRAY_BYTE_INDEX_SCALE); + + UNSAFE.getChar(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET); + UNSAFE.getChar(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 4L * Unsafe.ARRAY_CHAR_INDEX_SCALE); + + UNSAFE.getCharVolatile(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET); + UNSAFE.getCharVolatile( + new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 4L * Unsafe.ARRAY_CHAR_INDEX_SCALE); + + UNSAFE.getDouble(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET); + UNSAFE.getDouble( + new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 4L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + + UNSAFE.getDoubleVolatile(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET); + UNSAFE.getDoubleVolatile( + new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 4L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + + UNSAFE.getFloat(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET); + UNSAFE.getFloat( + new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * Unsafe.ARRAY_FLOAT_INDEX_SCALE); + + UNSAFE.getFloatVolatile(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET); + UNSAFE.getFloatVolatile( + new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * Unsafe.ARRAY_FLOAT_INDEX_SCALE); + + UNSAFE.getInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET); + UNSAFE.getInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 4L * Unsafe.ARRAY_INT_INDEX_SCALE); + + UNSAFE.getIntVolatile(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET); + UNSAFE.getIntVolatile( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 4L * Unsafe.ARRAY_INT_INDEX_SCALE); + + UNSAFE.getLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET); + UNSAFE.getLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 4L * Unsafe.ARRAY_LONG_INDEX_SCALE); + + UNSAFE.getLongVolatile(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET); + UNSAFE.getLongVolatile( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 4L * Unsafe.ARRAY_LONG_INDEX_SCALE); + + UNSAFE.getObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET); + UNSAFE.getObject( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 4L * Unsafe.ARRAY_OBJECT_INDEX_SCALE); + + UNSAFE.getObjectVolatile(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET); + UNSAFE.getObjectVolatile( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 4L * Unsafe.ARRAY_OBJECT_INDEX_SCALE); + + UNSAFE.getShort(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET); + UNSAFE.getShort( + new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET + 4L * Unsafe.ARRAY_SHORT_INDEX_SCALE); + + UNSAFE.getShortVolatile(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET); + UNSAFE.getShortVolatile( + new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET + 4L * Unsafe.ARRAY_SHORT_INDEX_SCALE); + + UNSAFE.putBoolean(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET, true); + UNSAFE.putBoolean( + new boolean[5], + Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 4L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE, + true); + + UNSAFE.putBooleanVolatile(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET, true); + UNSAFE.putBooleanVolatile( + new boolean[5], + Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 4L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE, + true); + + UNSAFE.putByte(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET, (byte) 0); + UNSAFE.putByte( + new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET + 4L * Unsafe.ARRAY_BYTE_INDEX_SCALE, (byte) 0); + + UNSAFE.putByteVolatile(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET, (byte) 0); + UNSAFE.putByteVolatile( + new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET + 4L * Unsafe.ARRAY_BYTE_INDEX_SCALE, (byte) 0); + + UNSAFE.putChar(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET, 'a'); + UNSAFE.putChar( + new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 4L * Unsafe.ARRAY_CHAR_INDEX_SCALE, 'a'); + + UNSAFE.putCharVolatile(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET, 'a'); + UNSAFE.putCharVolatile( + new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 4L * Unsafe.ARRAY_CHAR_INDEX_SCALE, 'a'); + + UNSAFE.putDouble(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET, 0); + UNSAFE.putDouble( + new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 4L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, 0); + + UNSAFE.putDoubleVolatile(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET, 0); + UNSAFE.putDoubleVolatile( + new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 4L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, 0); + + UNSAFE.putFloat(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET, 0); + UNSAFE.putFloat( + new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * Unsafe.ARRAY_FLOAT_INDEX_SCALE, 0); + + UNSAFE.putFloatVolatile(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET, 0); + UNSAFE.putFloatVolatile( + new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * Unsafe.ARRAY_FLOAT_INDEX_SCALE, 0); + + UNSAFE.putInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET, 0); + UNSAFE.putInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 4L * Unsafe.ARRAY_INT_INDEX_SCALE, 0); + + UNSAFE.putIntVolatile(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET, 0); + UNSAFE.putIntVolatile( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 4L * Unsafe.ARRAY_INT_INDEX_SCALE, 0); + + UNSAFE.putLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET, 0); + UNSAFE.putLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 4L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0); + + UNSAFE.putLongVolatile(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET, 0); + UNSAFE.putLongVolatile( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 4L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0); + + UNSAFE.putObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET, 0); + UNSAFE.putObject( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 4L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, 0); + + UNSAFE.putObjectVolatile(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET, 0); + UNSAFE.putObjectVolatile( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 4L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, 0); + + UNSAFE.putOrderedInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET, 0); + UNSAFE.putOrderedInt( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 4L * Unsafe.ARRAY_INT_INDEX_SCALE, 0); + + UNSAFE.putOrderedLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET, 0); + UNSAFE.putOrderedLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 4L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0); + + UNSAFE.putOrderedObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET, 0); + UNSAFE.putOrderedObject( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 4L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, 0); + + UNSAFE.putShort(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET, (short) 0); + UNSAFE.putShort( + new short[5], + Unsafe.ARRAY_SHORT_BASE_OFFSET + 4L * Unsafe.ARRAY_SHORT_INDEX_SCALE, + (short) 0); + + UNSAFE.putShortVolatile(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET, (short) 0); + UNSAFE.putShortVolatile( + new short[5], + Unsafe.ARRAY_SHORT_BASE_OFFSET + 4L * Unsafe.ARRAY_SHORT_INDEX_SCALE, + (short) 0); + + UNSAFE.copyMemory( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET, + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET + 3L * Unsafe.ARRAY_BYTE_INDEX_SCALE, + 2); + UNSAFE.copyMemory( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET + 3L * Unsafe.ARRAY_BYTE_INDEX_SCALE, + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET, + 2); + + UNSAFE.setMemory(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET, 2, (byte) 0); + UNSAFE.setMemory( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET + 3L * Unsafe.ARRAY_BYTE_INDEX_SCALE, + 2, + (byte) 0); + + // Assumes that byte[] address is aligned for 8-byte long access, or platform supports unaligned + // access + UNSAFE.getLong(new byte[8], Unsafe.ARRAY_BYTE_BASE_OFFSET); + UNSAFE.getLong( + new byte[16], Unsafe.ARRAY_BYTE_BASE_OFFSET + 8L * Unsafe.ARRAY_BYTE_INDEX_SCALE); + + long address = UNSAFE.allocateMemory(10); + // Native memory access with `null` as object should be unaffected + UNSAFE.putLong(null, address, 1); + UNSAFE.getLong(null, address); + UNSAFE.freeMemory(address); + + // Field access should be unaffected + class Dummy { + int i; + } + Field f = Dummy.class.getDeclaredField("i"); + long offset = UNSAFE.objectFieldOffset(f); + UNSAFE.putInt(new Dummy(), offset, 1); + UNSAFE.getInt(new Dummy(), offset); + } +} From 507eeaa9c6d66f2b0753182dff260e8b1af5f7f0 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Thu, 3 Jul 2025 18:06:19 +0200 Subject: [PATCH 2/9] Rename `getAccessSize` --- .../sanitizers/UnsafeArrayOutOfBounds.java | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java index 912063312..1d0b29656 100644 --- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java @@ -22,7 +22,6 @@ import com.code_intelligence.jazzer.utils.UnsafeProvider; import java.lang.invoke.MethodHandle; import java.lang.reflect.Array; -import sun.jvm.hotspot.utilities.AssertionFailure; import sun.misc.Unsafe; /** @@ -89,23 +88,23 @@ public class UnsafeArrayOutOfBounds { ==================================================== */ - private static int getAccessSize(Class c) { - int accessSize; + private static int getBytesCount(Class c) { + int bytesCount; if (c == boolean.class) { // Assumes that `Unsafe.ARRAY_BOOLEAN_INDEX_SCALE > 0` - accessSize = 1; + bytesCount = 1; } else if (c == byte.class) { - accessSize = 1; + bytesCount = 1; } else if (c == char.class || c == short.class) { - accessSize = 2; + bytesCount = 2; } else if (c == int.class || c == float.class) { - accessSize = 4; + bytesCount = 4; } else if (c == long.class || c == double.class) { - accessSize = 8; + bytesCount = 8; } else { - throw new AssertionFailure("Unexpected type: " + c); + throw new AssertionError("Unexpected type: " + c); } - return accessSize; + return bytesCount; } /** Hook for all {@link Unsafe} methods which read or write an {@code Object} reference. */ @@ -214,7 +213,7 @@ public static void objectAccessHook( targetMethod = "getShortVolatile") public static void primitiveGetterHook( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { - int accessSize = getAccessSize(method.type().returnType()); + int accessSize = getBytesCount(method.type().returnType()); checkAccess(arguments, accessSize); } @@ -306,7 +305,7 @@ public static void primitiveGetterHook( public static void primitiveSetterHook( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { int accessSize = - getAccessSize(method.type().parameterType(2 + 1)); // + 1 for implicit Unsafe instance + getBytesCount(method.type().parameterType(2 + 1)); // + 1 for implicit Unsafe instance checkAccess(arguments, accessSize); } From 3dc54519c97c0b3f3b559b260bcc775b311a3816 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Thu, 3 Jul 2025 20:51:14 +0200 Subject: [PATCH 3/9] Fix wrong method descriptors --- .../sanitizers/UnsafeArrayOutOfBounds.java | 41 ++++++++++--------- .../jazzer/api/MethodHook.java | 5 ++- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java index 1d0b29656..8c1e082f8 100644 --- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java @@ -75,7 +75,10 @@ public class UnsafeArrayOutOfBounds { if (duplicateMethodNames.contains(methodName)) { String methodDesc; try { - methodDesc = MethodHandles.lookup().unreflect(m).type().toMethodDescriptorString(); + methodDesc = MethodHandles.lookup().unreflect(m).type() + // Drop receiver type (i.e. `Unsafe this`) + .dropParameterTypes(0, 1) + .toMethodDescriptorString(); } catch (IllegalAccessException e) { throw new RuntimeException(e); } @@ -152,7 +155,7 @@ public static void objectAccessHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getByte", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)B") + targetMethodDescriptor = "(Ljava/lang/Object;J)B") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -161,7 +164,7 @@ public static void objectAccessHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getChar", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)C") + targetMethodDescriptor = "(Ljava/lang/Object;J)C") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -170,7 +173,7 @@ public static void objectAccessHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getDouble", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)D") + targetMethodDescriptor = "(Ljava/lang/Object;J)D") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -179,7 +182,7 @@ public static void objectAccessHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getFloat", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)F") + targetMethodDescriptor = "(Ljava/lang/Object;J)F") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -188,7 +191,7 @@ public static void objectAccessHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getInt", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)I") + targetMethodDescriptor = "(Ljava/lang/Object;J)I") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -197,7 +200,7 @@ public static void objectAccessHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getLong", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)J") + targetMethodDescriptor = "(Ljava/lang/Object;J)J") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -206,7 +209,7 @@ public static void objectAccessHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getShort", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;J)S") + targetMethodDescriptor = "(Ljava/lang/Object;J)S") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -238,7 +241,7 @@ public static void primitiveGetterHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "putByte", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JB)V") + targetMethodDescriptor = "(Ljava/lang/Object;JB)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -247,7 +250,7 @@ public static void primitiveGetterHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "putChar", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JC)V") + targetMethodDescriptor = "(Ljava/lang/Object;JC)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -256,7 +259,7 @@ public static void primitiveGetterHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "putDouble", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JD)V") + targetMethodDescriptor = "(Ljava/lang/Object;JD)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -265,7 +268,7 @@ public static void primitiveGetterHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "putFloat", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JF)V") + targetMethodDescriptor = "(Ljava/lang/Object;JF)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -274,7 +277,7 @@ public static void primitiveGetterHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "putInt", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JI)V") + targetMethodDescriptor = "(Ljava/lang/Object;JI)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -283,7 +286,7 @@ public static void primitiveGetterHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "putLong", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JJ)V") + targetMethodDescriptor = "(Ljava/lang/Object;JJ)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -292,7 +295,7 @@ public static void primitiveGetterHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "putShort", - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JS)V") + targetMethodDescriptor = "(Ljava/lang/Object;JS)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -315,7 +318,7 @@ public static void primitiveSetterHook( targetClassName = UNSAFE_NAME, targetMethod = "setMemory", // Only handle overload with Object parameter - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JJB)V") + targetMethodDescriptor = "(Ljava/lang/Object;JJB)V") public static void setMemoryHook( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { checkAccess(arguments[0], (long) arguments[1], (long) arguments[2]); @@ -327,7 +330,7 @@ public static void setMemoryHook( targetClassName = UNSAFE_NAME, targetMethod = "copyMemory", // Only handle overload with Object parameters - targetMethodDescriptor = "(Lsun/misc/Unsafe;Ljava/lang/Object;JLjava/lang/Object;JJ)V") + targetMethodDescriptor = "(Ljava/lang/Object;JLjava/lang/Object;JJ)V") public static void copyMemoryHook( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { long size = (long) arguments[4]; @@ -383,7 +386,7 @@ private static void checkAccess(Object obj, long offset, long accessSize) { long indexScale = UNSAFE.arrayIndexScale(objClass); if (offset < baseOffset) { - report("Offset " + offset + " lower than baseOffset " + baseOffset); + report("Offset " + offset + " is lower than baseOffset " + baseOffset); } long endOffset = baseOffset + Array.getLength(obj) * indexScale; // Uses `compareUnsigned` to account for overflow @@ -439,7 +442,7 @@ private static void checkObjectSizedAccess(Object obj, long offset) { long indexScale = UNSAFE.arrayIndexScale(objClass); if (offset < baseOffset) { - report("Offset " + offset + " lower than baseOffset " + baseOffset); + report("Offset " + offset + " is lower than baseOffset " + baseOffset); } if ((offset - baseOffset) % indexScale != 0) { diff --git a/src/main/java/com/code_intelligence/jazzer/api/MethodHook.java b/src/main/java/com/code_intelligence/jazzer/api/MethodHook.java index bb1a8a405..d37163e59 100644 --- a/src/main/java/com/code_intelligence/jazzer/api/MethodHook.java +++ b/src/main/java/com/code_intelligence/jazzer/api/MethodHook.java @@ -192,7 +192,10 @@ *

The descriptor of a method is an internal representation of the method's signature, which * includes the types of its parameters and its return value. For more information on descriptors, * see the JVM - * Specification, Section 4.3.3 and {@link MethodType#toMethodDescriptorString()} + * Specification, Section 4.3.3 and {@link MethodType#toMethodDescriptorString()}. + * + *

Important: For instance methods the parameter for the argument representing {@code + * this} has to be omitted. * * @return the descriptor of the method to be hooked */ From feace131c249ed270e30a71e08fb0b5e54eae6ee Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Fri, 4 Jul 2025 13:11:19 +0200 Subject: [PATCH 4/9] Make compatible with Java 8 --- .../sanitizers/UnsafeArrayOutOfBounds.java | 119 ++++++++++++++++-- 1 file changed, 107 insertions(+), 12 deletions(-) diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java index 8c1e082f8..f1c5b7abe 100644 --- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java @@ -55,24 +55,23 @@ public class UnsafeArrayOutOfBounds { Script for generating the @MethodHook annotations: ==================================================== Method[] methods = sun.misc.Unsafe.class.getMethods(); - Set allMethodNames = new HashSet<>(); - Set duplicateMethodNames = new HashSet<>(); - Arrays.stream(methods).map(Method::getName).forEach(n -> { - if (!allMethodNames.add(n)) { - duplicateMethodNames.add(n); - } - }); + Set methodsWithNativeOnlyOverload = Arrays.stream(methods) + .filter(m -> Arrays.stream(m.getParameterTypes()).noneMatch(p -> p == Object.class)) + .map(Method::getName) + .collect(Collectors.toSet()); + Set emittedWithoutDesc = new HashSet<>(); Arrays.stream(methods) .filter(m -> Arrays.stream(m.getParameterTypes()).anyMatch(p -> p == Object.class)) - .filter(m -> !Set.of("equals", "unpark").contains(m.getName())) + .filter(m -> !Arrays.asList("equals", "monitorEnter", "monitorExit", "tryMonitorEnter", "unpark") + .contains(m.getName())) .sorted(Comparator.comparing(Method::getName)) .forEach(m -> { String methodName = m.getName(); String s = "@MethodHook(\n type = HookType.BEFORE,\n targetClassName = UNSAFE_NAME,\n targetMethod = \"" + methodName + "\""; - if (duplicateMethodNames.contains(methodName)) { + if (methodsWithNativeOnlyOverload.contains(methodName)) { String methodDesc; try { methodDesc = MethodHandles.lookup().unreflect(m).type() @@ -85,8 +84,12 @@ public class UnsafeArrayOutOfBounds { s += ",\n targetMethodDescriptor = \"" + methodDesc + "\""; + System.out.println(s + ")"); + } + // Avoid emitting same annotation twice for overloads where no native-only overload exists + else if (emittedWithoutDesc.add(methodName)) { + System.out.println(s + ")"); } - System.out.println(s + ")"); }); ==================================================== */ @@ -146,6 +149,7 @@ public static void objectAccessHook( @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getAndAddLong") @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getAndSetInt") @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getAndSetLong") + // `getBoolean` has no overload without Object parameter, so no need to specify method descriptor @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "getBoolean") @MethodHook( type = HookType.BEFORE, @@ -156,6 +160,12 @@ public static void objectAccessHook( targetClassName = UNSAFE_NAME, targetMethod = "getByte", targetMethodDescriptor = "(Ljava/lang/Object;J)B") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getByte", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;I)B") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -165,6 +175,12 @@ public static void objectAccessHook( targetClassName = UNSAFE_NAME, targetMethod = "getChar", targetMethodDescriptor = "(Ljava/lang/Object;J)C") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getChar", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;I)C") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -174,6 +190,12 @@ public static void objectAccessHook( targetClassName = UNSAFE_NAME, targetMethod = "getDouble", targetMethodDescriptor = "(Ljava/lang/Object;J)D") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getDouble", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;I)D") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -183,6 +205,12 @@ public static void objectAccessHook( targetClassName = UNSAFE_NAME, targetMethod = "getFloat", targetMethodDescriptor = "(Ljava/lang/Object;J)F") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getFloat", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;I)F") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -192,6 +220,12 @@ public static void objectAccessHook( targetClassName = UNSAFE_NAME, targetMethod = "getInt", targetMethodDescriptor = "(Ljava/lang/Object;J)I") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getInt", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;I)I") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -201,6 +235,12 @@ public static void objectAccessHook( targetClassName = UNSAFE_NAME, targetMethod = "getLong", targetMethodDescriptor = "(Ljava/lang/Object;J)J") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getLong", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;I)J") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -210,6 +250,12 @@ public static void objectAccessHook( targetClassName = UNSAFE_NAME, targetMethod = "getShort", targetMethodDescriptor = "(Ljava/lang/Object;J)S") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "getShort", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;I)S") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -232,6 +278,7 @@ public static void primitiveGetterHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "compareAndSwapLong") + // `putBoolean` has no overload without Object parameter, so no need to specify method descriptor @MethodHook(type = HookType.BEFORE, targetClassName = UNSAFE_NAME, targetMethod = "putBoolean") @MethodHook( type = HookType.BEFORE, @@ -242,6 +289,12 @@ public static void primitiveGetterHook( targetClassName = UNSAFE_NAME, targetMethod = "putByte", targetMethodDescriptor = "(Ljava/lang/Object;JB)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putByte", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;IB)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -251,6 +304,12 @@ public static void primitiveGetterHook( targetClassName = UNSAFE_NAME, targetMethod = "putChar", targetMethodDescriptor = "(Ljava/lang/Object;JC)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putChar", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;IC)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -260,6 +319,12 @@ public static void primitiveGetterHook( targetClassName = UNSAFE_NAME, targetMethod = "putDouble", targetMethodDescriptor = "(Ljava/lang/Object;JD)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putDouble", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;ID)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -269,6 +334,12 @@ public static void primitiveGetterHook( targetClassName = UNSAFE_NAME, targetMethod = "putFloat", targetMethodDescriptor = "(Ljava/lang/Object;JF)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putFloat", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;IF)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -278,6 +349,12 @@ public static void primitiveGetterHook( targetClassName = UNSAFE_NAME, targetMethod = "putInt", targetMethodDescriptor = "(Ljava/lang/Object;JI)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putInt", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;II)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -287,6 +364,12 @@ public static void primitiveGetterHook( targetClassName = UNSAFE_NAME, targetMethod = "putLong", targetMethodDescriptor = "(Ljava/lang/Object;JJ)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putLong", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;IJ)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -296,6 +379,12 @@ public static void primitiveGetterHook( targetClassName = UNSAFE_NAME, targetMethod = "putShort", targetMethodDescriptor = "(Ljava/lang/Object;JS)V") + @MethodHook( + type = HookType.BEFORE, + targetClassName = UNSAFE_NAME, + targetMethod = "putShort", + // Overload with `int offset`, removed in Java 9 + targetMethodDescriptor = "(Ljava/lang/Object;IS)V") @MethodHook( type = HookType.BEFORE, targetClassName = UNSAFE_NAME, @@ -342,9 +431,15 @@ private static void report(String message) { Jazzer.reportFindingFromHook(new FuzzerSecurityIssueCritical(message)); } + private static long offsetValue(Object obj) { + // Java 8 also had deprecated Unsafe method overloads with `int offset` parameter, therefore + // cannot just cast to `long` here + return ((Number) obj).longValue(); + } + private static void checkAccess(Object[] args, long accessSize) { Object obj = args[0]; - long offset = (long) args[1]; + long offset = offsetValue(args[1]); checkAccess(obj, offset, accessSize); } @@ -406,7 +501,7 @@ private static void checkAccess(Object obj, long offset, long accessSize) { private static void checkObjectSizedAccess(Object[] args) { Object obj = args[0]; - long offset = (long) args[1]; + long offset = offsetValue(args[1]); checkObjectSizedAccess(obj, offset); } From 235f0384d1842e1ccd85a66abea72c66e0882d70 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Wed, 16 Jul 2025 00:08:05 +0200 Subject: [PATCH 5/9] Rename sanitizer & merge check implementation --- sanitizers/sanitizers.bzl | 2 +- .../jazzer/sanitizers/BUILD.bazel | 6 +- ...yOutOfBounds.java => UnsafeSanitizer.java} | 110 +++++++----------- ...BoundsValid.java => UnsafeArrayValid.java} | 2 +- 4 files changed, 44 insertions(+), 76 deletions(-) rename sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/{UnsafeArrayOutOfBounds.java => UnsafeSanitizer.java} (84%) rename sanitizers/src/test/java/com/example/{UnsafeArrayOutOfBoundsValid.java => UnsafeArrayValid.java} (97%) diff --git a/sanitizers/sanitizers.bzl b/sanitizers/sanitizers.bzl index 3b17c1bb2..fbf7bec65 100644 --- a/sanitizers/sanitizers.bzl +++ b/sanitizers/sanitizers.bzl @@ -31,7 +31,7 @@ _sanitizer_class_names = [ "ScriptEngineInjection", "ServerSideRequestForgery", "SqlInjection", - "UnsafeArrayOutOfBounds", + "UnsafeSanitizer", "XPathInjection", ] diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel index 2de8432c8..3711d89c0 100644 --- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel @@ -49,8 +49,8 @@ java_library( ) java_library( - name = "unsafe_array_out_of_bounds", - srcs = ["UnsafeArrayOutOfBounds.java"], + name = "unsafe_sanitizer", + srcs = ["UnsafeSanitizer.java"], deps = [ "//src/main/java/com/code_intelligence/jazzer/api:hooks", "//src/main/java/com/code_intelligence/jazzer/utils:unsafe_provider", @@ -78,7 +78,7 @@ kt_jvm_library( ":script_engine_injection", ":server_side_request_forgery", ":sql_injection", - ":unsafe_array_out_of_bounds", + ":unsafe_sanitizer", ], deps = [ "//src/main/java/com/code_intelligence/jazzer/api:hooks", diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeSanitizer.java similarity index 84% rename from sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java rename to sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeSanitizer.java index f1c5b7abe..4cf22425d 100644 --- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeArrayOutOfBounds.java +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeSanitizer.java @@ -24,11 +24,8 @@ import java.lang.reflect.Array; import sun.misc.Unsafe; -/** - * Sanitizer for {@link Unsafe sun.misc.Unsafe} usage which performs out-of-bounds and some other - * invalid access on arrays. - */ -public class UnsafeArrayOutOfBounds { +/** Sanitizer for {@link Unsafe sun.misc.Unsafe} usage. */ +public class UnsafeSanitizer { /* * Implementation notes: * - This only covers the 'public' class sun.misc.Unsafe, not the JDK-internal jdk.internal.misc.Unsafe since @@ -263,7 +260,7 @@ public static void objectAccessHook( public static void primitiveGetterHook( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { int accessSize = getBytesCount(method.type().returnType()); - checkAccess(arguments, accessSize); + checkPrimitiveAccess(arguments, accessSize); } /** @@ -398,7 +395,7 @@ public static void primitiveSetterHook( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { int accessSize = getBytesCount(method.type().parameterType(2 + 1)); // + 1 for implicit Unsafe instance - checkAccess(arguments, accessSize); + checkPrimitiveAccess(arguments, accessSize); } /** Hook for {@link Unsafe#setMemory(Object, long, long, byte)} */ @@ -410,7 +407,7 @@ public static void primitiveSetterHook( targetMethodDescriptor = "(Ljava/lang/Object;JJB)V") public static void setMemoryHook( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { - checkAccess(arguments[0], (long) arguments[1], (long) arguments[2]); + checkPrimitiveAccess(arguments[0], (long) arguments[1], (long) arguments[2]); } /** Hook for {@link Unsafe#copyMemory(Object, long, Object, long, long)} */ @@ -423,8 +420,8 @@ public static void setMemoryHook( public static void copyMemoryHook( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { long size = (long) arguments[4]; - checkAccess(arguments[0], (long) arguments[1], size); - checkAccess(arguments[2], (long) arguments[3], size); + checkPrimitiveAccess(arguments[0], (long) arguments[1], size); + checkPrimitiveAccess(arguments[2], (long) arguments[3], size); } private static void report(String message) { @@ -437,14 +434,25 @@ private static long offsetValue(Object obj) { return ((Number) obj).longValue(); } - private static void checkAccess(Object[] args, long accessSize) { + private static void checkPrimitiveAccess(Object[] args, long accessSize) { Object obj = args[0]; long offset = offsetValue(args[1]); - checkAccess(obj, offset, accessSize); + checkPrimitiveAccess(obj, offset, accessSize); + } + + private static void checkPrimitiveAccess(Object obj, long offset, long accessSize) { + checkAccess(obj, offset, accessSize, false); + } + + private static void checkObjectSizedAccess(Object[] args) { + Object obj = args[0]; + long offset = offsetValue(args[1]); + long accessSize = Unsafe.ARRAY_OBJECT_INDEX_SCALE; + checkAccess(obj, offset, accessSize, true); } /** - * Checks {@link Unsafe} memory access where the access size is measured in number of bytes. + * Checks {@link Unsafe} memory access. * * @param obj the base object for memory access; e.g. for {@link Unsafe#getInt(Object, long)} it * is the argument at index 0 @@ -452,9 +460,11 @@ private static void checkAccess(Object[] args, long accessSize) { * is the argument at index 1 * @param accessSize the number of bytes which is accessed; e.g. for {@link Unsafe#getInt(Object, * long)} it is 4 (due to {@code int} being 4 bytes large) - * @see #checkObjectSizedAccess(Object, long) + * @param isObjectAccess whether an object reference (instead of a primitive value) is being + * accessed */ - private static void checkAccess(Object obj, long offset, long accessSize) { + private static void checkAccess( + Object obj, long offset, long accessSize, boolean isObjectAccess) { if (accessSize < 0) { report("Negative access size: " + accessSize); } @@ -471,10 +481,17 @@ private static void checkAccess(Object obj, long offset, long accessSize) { return; } - if (!componentType.isPrimitive()) { - // Reading or writing bytes to an array of references; might be possible but seems - // rather unreliable and might mess with the garbage collector? - report("Reading or writing bytes from a " + objClass.getTypeName()); + // Mixing up bytes and object references (e.g. reading an object reference from a primitive + // array) + // seems error-prone and might mess with the garbage collector + if (isObjectAccess) { + if (componentType.isPrimitive()) { + report("Reading or writing object reference from a " + objClass.getTypeName()); + } + } else { + if (!componentType.isPrimitive()) { + report("Reading or writing bytes from a " + objClass.getTypeName()); + } } long baseOffset = UNSAFE.arrayBaseOffset(objClass); @@ -495,60 +512,11 @@ private static void checkAccess(Object obj, long offset, long accessSize) { + endOffset); } - // Don't check if access is aligned; depends on platform if unaligned access is supported - // and some libraries which are using Unsafe assume that it is supported - } - - private static void checkObjectSizedAccess(Object[] args) { - Object obj = args[0]; - long offset = offsetValue(args[1]); - checkObjectSizedAccess(obj, offset); - } - - /** - * Checks {@link Unsafe} memory access where an object reference is accessed. - * - * @param obj the base object for memory access; e.g. for {@link Unsafe#getObject(Object, long)} - * it is the argument at index 0 - * @param offset the offset for the memory access; e.g. for {@link Unsafe#getObject(Object, long)} - * it is the argument at index 1 - * @see #checkAccess(Object, long, long) - */ - private static void checkObjectSizedAccess(Object obj, long offset) { - if (obj == null) { - // Native memory access; not sanitized here - return; - } - - Class objClass = obj.getClass(); - Class componentType = objClass.getComponentType(); - if (componentType == null) { - // Not an array - return; - } - - if (componentType.isPrimitive()) { - // Reading or writing object references from a primitive array; might be possible but seems - // rather unreliable and might mess with the garbage collector? - report("Reading or writing object reference from a " + objClass.getTypeName()); - } - - long baseOffset = UNSAFE.arrayBaseOffset(objClass); - long indexScale = UNSAFE.arrayIndexScale(objClass); - - if (offset < baseOffset) { - report("Offset " + offset + " is lower than baseOffset " + baseOffset); - } - - if ((offset - baseOffset) % indexScale != 0) { + if (isObjectAccess && (offset - baseOffset) % indexScale != 0) { // Trying to read or write object at an offset which spans two array elements report("Access at offset " + offset + " is not aligned"); } - - long endOffset = baseOffset + Array.getLength(obj) * indexScale; - long accessSize = indexScale; - if (Long.compareUnsigned(endOffset, offset + accessSize) < 0) { - report("Access at offset " + offset + " exceeds end offset " + endOffset); - } + // For primitive arrays don't check if access is aligned; depends on platform if unaligned + // access is supported and some libraries which are using Unsafe assume that it is supported } } diff --git a/sanitizers/src/test/java/com/example/UnsafeArrayOutOfBoundsValid.java b/sanitizers/src/test/java/com/example/UnsafeArrayValid.java similarity index 97% rename from sanitizers/src/test/java/com/example/UnsafeArrayOutOfBoundsValid.java rename to sanitizers/src/test/java/com/example/UnsafeArrayValid.java index 62d937ba2..6be8f34da 100644 --- a/sanitizers/src/test/java/com/example/UnsafeArrayOutOfBoundsValid.java +++ b/sanitizers/src/test/java/com/example/UnsafeArrayValid.java @@ -20,7 +20,7 @@ import sun.misc.Unsafe; /** Verifies that valid {@link Unsafe} usage does not cause a spurious sanitizer exception. */ -public class UnsafeArrayOutOfBoundsValid { +public class UnsafeArrayValid { private static final Unsafe UNSAFE; static { From 6c0a98d2fa961fc1b91e5b8e0c8cec8dbb9d935a Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Wed, 16 Jul 2025 00:08:28 +0200 Subject: [PATCH 6/9] Refactor test execution --- .../src/test/java/com/example/BUILD.bazel | 131 ++- .../com/example/UnsafeArrayOutOfBounds.java | 907 ++++++++++-------- 2 files changed, 619 insertions(+), 419 deletions(-) diff --git a/sanitizers/src/test/java/com/example/BUILD.bazel b/sanitizers/src/test/java/com/example/BUILD.bazel index 67b47831b..a270d31c2 100644 --- a/sanitizers/src/test/java/com/example/BUILD.bazel +++ b/sanitizers/src/test/java/com/example/BUILD.bazel @@ -218,29 +218,148 @@ java_fuzz_target_test( ], ) -java_fuzz_target_test( - name = "UnsafeArrayOutOfBounds", +[java_fuzz_target_test( + name = "UnsafeArrayOutOfBounds_" + method, srcs = [ "UnsafeArrayOutOfBounds.java", ], + fuzzer_args = [ + "-print_final_stats=1", + "-runs=0", + ], + env = { + "JAZZER_FUZZ": "1", + }, allowed_findings = [ "com.code_intelligence.jazzer.api.FuzzerSecurityIssueCritical", ], + expect_number_of_findings=1, target_class = "com.example.UnsafeArrayOutOfBounds", + target_method = method, verify_crash_reproducer = False, -) + runtime_deps = [ + "@maven//:org_junit_jupiter_junit_jupiter_engine", + ], + deps = [ + "//deploy:jazzer-junit", + ], +) for method in [ + "byteAccessOnObjectArray", + "compareAndSwapInt", + "compareAndSwapInt_end", + "compareAndSwapLong", + "compareAndSwapLong_end", + "compareAndSwapObject", + "compareAndSwapObject_end", + "copyMemory", + "copyMemory_end", + "copyMemory_dest", + "copyMemory_dest_end", + "getAndAddInt", + "getAndAddInt_end", + "getAndAddLong", + "getAndAddLong_end", + "getAndSetInt", + "getAndSetInt_end", + "getAndSetLong", + "getAndSetLong_end", + "getAndSetObject", + "getAndSetObject_end", + "getBoolean", + "getBoolean_end", + "getBooleanVolatile", + "getBooleanVolatile_end", + "getByte", + "getByte_end", + "getByteVolatile", + "getByteVolatile_end", + "getChar", + "getChar_end", + "getCharVolatile", + "getCharVolatile_end", + "getDouble", + "getDouble_end", + "getDoubleVolatile", + "getDoubleVolatile_end", + "getFloat", + "getFloat_end", + "getFloatVolatile", + "getFloatVolatile_end", + "getInt", + "getInt_end", + "getIntVolatile", + "getIntVolatile_end", + "getLong", + "getLong_end", + "getLongVolatile", + "getLongVolatile_end", + "getObject", + "getObject_end", + "getObjectVolatile", + "getObjectVolatile_end", + "getShort", + "getShort_end", + "getShortVolatile", + "getShortVolatile_end", + "objectAccessOnPrimitiveArray", + "putBoolean", + "putBoolean_end", + "putBooleanVolatile", + "putBooleanVolatile_end", + "putByte", + "putByte_end", + "putByteVolatile", + "putByteVolatile_end", + "putChar", + "putChar_end", + "putCharVolatile", + "putCharVolatile_end", + "putDouble", + "putDouble_end", + "putDoubleVolatile", + "putDoubleVolatile_end", + "putFloat", + "putFloat_end", + "putFloatVolatile", + "putFloatVolatile_end", + "putInt", + "putInt_end", + "putIntVolatile", + "putIntVolatile_end", + "putLong", + "putLong_end", + "putLongVolatile", + "putLongVolatile_end", + "putObject", + "putObject_end", + "putObjectVolatile", + "putObjectVolatile_end", + "putOrderedInt", + "putOrderedInt_end", + "putOrderedLong", + "putOrderedLong_end", + "putOrderedObject", + "putOrderedObject_end", + "putShort", + "putShort_end", + "putShortVolatile", + "putShortVolatile_end", + "setMemory", + "setMemory_end", + "unalignedObjectAccess", +]] java_fuzz_target_test( - name = "UnsafeArrayOutOfBoundsValid", + name = "UnsafeArrayValid", srcs = [ - "UnsafeArrayOutOfBoundsValid.java", + "UnsafeArrayValid.java", ], allowed_findings = [], fuzzer_args = [ # Test does not depend on fuzzing input; just run it once "-runs=1", ], - target_class = "com.example.UnsafeArrayOutOfBoundsValid", + target_class = "com.example.UnsafeArrayValid", ) java_fuzz_target_test( diff --git a/sanitizers/src/test/java/com/example/UnsafeArrayOutOfBounds.java b/sanitizers/src/test/java/com/example/UnsafeArrayOutOfBounds.java index a33b74c89..a166784e2 100644 --- a/sanitizers/src/test/java/com/example/UnsafeArrayOutOfBounds.java +++ b/sanitizers/src/test/java/com/example/UnsafeArrayOutOfBounds.java @@ -15,9 +15,8 @@ */ package com.example; -import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.junit.FuzzTest; import java.lang.reflect.Field; -import java.lang.reflect.Method; import sun.misc.Unsafe; public class UnsafeArrayOutOfBounds { @@ -33,520 +32,602 @@ public class UnsafeArrayOutOfBounds { } } - /** Defines test methods which all perform invalid memory access with {@link Unsafe}. */ - @SuppressWarnings("unused") // test methods are accessed through reflection - private static class TestMethods { - static void compareAndSwapInt() { - UNSAFE.compareAndSwapInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 0, 1); - } - - static void compareAndSwapInt_end() { - UNSAFE.compareAndSwapInt( - new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 0, 1); - } + /* + * These test methods all perform invalid memory access with Unsafe. + * IMPORTANT: When adding new methods, the list of method names in `BUILD.bazel` has to be adjusted as well. + */ - static void compareAndSwapLong() { - UNSAFE.compareAndSwapLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 0, 1); - } - - static void compareAndSwapLong_end() { - UNSAFE.compareAndSwapLong( - new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0, 1); - } + @FuzzTest + public void compareAndSwapInt(Boolean ignored) { + UNSAFE.compareAndSwapInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 0, 1); + } - static void compareAndSwapObject() { - UNSAFE.compareAndSwapObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, "a", "b"); - } + @FuzzTest + public void compareAndSwapInt_end(Boolean ignored) { + UNSAFE.compareAndSwapInt( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 0, 1); + } - static void compareAndSwapObject_end() { - UNSAFE.compareAndSwapObject( - new Object[5], - Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, - "a", - "b"); - } + @FuzzTest + public void compareAndSwapLong(Boolean ignored) { + UNSAFE.compareAndSwapLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 0, 1); + } - static void getAndAddInt() { - UNSAFE.getAndAddInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 1); - } + @FuzzTest + public void compareAndSwapLong_end(Boolean ignored) { + UNSAFE.compareAndSwapLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0, 1); + } - static void getAndAddInt_end() { - UNSAFE.getAndAddInt( - new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 1); - } + @FuzzTest + public void compareAndSwapObject(Boolean ignored) { + UNSAFE.compareAndSwapObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, "a", "b"); + } - static void getAndAddLong() { - UNSAFE.getAndAddLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 1); - } + @FuzzTest + public void compareAndSwapObject_end(Boolean ignored) { + UNSAFE.compareAndSwapObject( + new Object[5], + Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, + "a", + "b"); + } - static void getAndAddLong_end() { - UNSAFE.getAndAddLong( - new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 1); - } + @FuzzTest + public void getAndAddInt(Boolean ignored) { + UNSAFE.getAndAddInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 1); + } - static void getAndSetInt() { - UNSAFE.getAndSetInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 1); - } + @FuzzTest + public void getAndAddInt_end(Boolean ignored) { + UNSAFE.getAndAddInt( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 1); + } - static void getAndSetInt_end() { - UNSAFE.getAndSetInt( - new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 1); - } + @FuzzTest + public void getAndAddLong(Boolean ignored) { + UNSAFE.getAndAddLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 1); + } - static void getAndSetLong() { - UNSAFE.getAndSetLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 1); - } + @FuzzTest + public void getAndAddLong_end(Boolean ignored) { + UNSAFE.getAndAddLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 1); + } - static void getAndSetLong_end() { - UNSAFE.getAndSetLong( - new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 1); - } + @FuzzTest + public void getAndSetInt(Boolean ignored) { + UNSAFE.getAndSetInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 1); + } - static void getAndSetObject() { - UNSAFE.getAndSetObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, "a"); - } + @FuzzTest + public void getAndSetInt_end(Boolean ignored) { + UNSAFE.getAndSetInt( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 1); + } - static void getAndSetObject_end() { - UNSAFE.getAndSetObject( - new Object[5], - Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, - "a"); - } + @FuzzTest + public void getAndSetLong(Boolean ignored) { + UNSAFE.getAndSetLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 1); + } - static void getBoolean() { - UNSAFE.getBoolean(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET - 1); - } + @FuzzTest + public void getAndSetLong_end(Boolean ignored) { + UNSAFE.getAndSetLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 1); + } - static void getBoolean_end() { - UNSAFE.getBoolean( - new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 5L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE); - } + @FuzzTest + public void getAndSetObject(Boolean ignored) { + UNSAFE.getAndSetObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, "a"); + } - static void getBooleanVolatile() { - UNSAFE.getBooleanVolatile(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET - 1); - } + @FuzzTest + public void getAndSetObject_end(Boolean ignored) { + UNSAFE.getAndSetObject( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, "a"); + } - static void getBooleanVolatile_end() { - UNSAFE.getBooleanVolatile( - new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 5L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE); - } + @FuzzTest + public void getBoolean(Boolean ignored) { + UNSAFE.getBoolean(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET - 1); + } - static void getByte() { - UNSAFE.getByte(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1); - } + @FuzzTest + public void getBoolean_end(Boolean ignored) { + UNSAFE.getBoolean( + new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 5L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE); + } - static void getByte_end() { - UNSAFE.getByte( - new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET + 5L * Unsafe.ARRAY_BYTE_INDEX_SCALE); - } + @FuzzTest + public void getBooleanVolatile(Boolean ignored) { + UNSAFE.getBooleanVolatile(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET - 1); + } - static void getByteVolatile() { - UNSAFE.getByteVolatile(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1); - } + @FuzzTest + public void getBooleanVolatile_end(Boolean ignored) { + UNSAFE.getBooleanVolatile( + new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 5L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE); + } - static void getByteVolatile_end() { - UNSAFE.getByteVolatile( - new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET + 5L * Unsafe.ARRAY_BYTE_INDEX_SCALE); - } + @FuzzTest + public void getByte(Boolean ignored) { + UNSAFE.getByte(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1); + } - static void getChar() { - UNSAFE.getChar(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET - 1); - } + @FuzzTest + public void getByte_end(Boolean ignored) { + UNSAFE.getByte(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET + 5L * Unsafe.ARRAY_BYTE_INDEX_SCALE); + } - static void getChar_end() { - UNSAFE.getChar( - new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 5L * Unsafe.ARRAY_CHAR_INDEX_SCALE); - } + @FuzzTest + public void getByteVolatile(Boolean ignored) { + UNSAFE.getByteVolatile(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1); + } - static void getCharVolatile() { - UNSAFE.getCharVolatile(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET - 1); - } + @FuzzTest + public void getByteVolatile_end(Boolean ignored) { + UNSAFE.getByteVolatile( + new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET + 5L * Unsafe.ARRAY_BYTE_INDEX_SCALE); + } - static void getCharVolatile_end() { - UNSAFE.getCharVolatile( - new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 5L * Unsafe.ARRAY_CHAR_INDEX_SCALE); - } + @FuzzTest + public void getChar(Boolean ignored) { + UNSAFE.getChar(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET - 1); + } - static void getDouble() { - UNSAFE.getDouble(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET - 1); - } + @FuzzTest + public void getChar_end(Boolean ignored) { + UNSAFE.getChar(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 5L * Unsafe.ARRAY_CHAR_INDEX_SCALE); + } - static void getDouble_end() { - UNSAFE.getDouble( - new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 5L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); - } + @FuzzTest + public void getCharVolatile(Boolean ignored) { + UNSAFE.getCharVolatile(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET - 1); + } - static void getDoubleVolatile() { - UNSAFE.getDoubleVolatile(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET - 1); - } + @FuzzTest + public void getCharVolatile_end(Boolean ignored) { + UNSAFE.getCharVolatile( + new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 5L * Unsafe.ARRAY_CHAR_INDEX_SCALE); + } - static void getDoubleVolatile_end() { - UNSAFE.getDoubleVolatile( - new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 5L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); - } + @FuzzTest + public void getDouble(Boolean ignored) { + UNSAFE.getDouble(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET - 1); + } - static void getFloat() { - UNSAFE.getFloat(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET - 1); - } + @FuzzTest + public void getDouble_end(Boolean ignored) { + UNSAFE.getDouble( + new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 5L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + } - static void getFloat_end() { - UNSAFE.getFloat( - new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 5L * Unsafe.ARRAY_FLOAT_INDEX_SCALE); - } + @FuzzTest + public void getDoubleVolatile(Boolean ignored) { + UNSAFE.getDoubleVolatile(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET - 1); + } - static void getFloatVolatile() { - UNSAFE.getFloatVolatile(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET - 1); - } + @FuzzTest + public void getDoubleVolatile_end(Boolean ignored) { + UNSAFE.getDoubleVolatile( + new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 5L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + } - static void getFloatVolatile_end() { - UNSAFE.getFloatVolatile( - new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 5L * Unsafe.ARRAY_FLOAT_INDEX_SCALE); - } + @FuzzTest + public void getFloat(Boolean ignored) { + UNSAFE.getFloat(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET - 1); + } - static void getInt() { - UNSAFE.getInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1); - } + @FuzzTest + public void getFloat_end(Boolean ignored) { + UNSAFE.getFloat( + new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 5L * Unsafe.ARRAY_FLOAT_INDEX_SCALE); + } - static void getInt_end() { - UNSAFE.getInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE); - } + @FuzzTest + public void getFloatVolatile(Boolean ignored) { + UNSAFE.getFloatVolatile(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET - 1); + } - static void getIntVolatile() { - UNSAFE.getIntVolatile(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1); - } + @FuzzTest + public void getFloatVolatile_end(Boolean ignored) { + UNSAFE.getFloatVolatile( + new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 5L * Unsafe.ARRAY_FLOAT_INDEX_SCALE); + } - static void getIntVolatile_end() { - UNSAFE.getIntVolatile( - new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE); - } + @FuzzTest + public void getInt(Boolean ignored) { + UNSAFE.getInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1); + } - static void getLong() { - UNSAFE.getLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1); - } + @FuzzTest + public void getInt_end(Boolean ignored) { + UNSAFE.getInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE); + } - static void getLong_end() { - UNSAFE.getLong( - new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE); - } + @FuzzTest + public void getIntVolatile(Boolean ignored) { + UNSAFE.getIntVolatile(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1); + } - static void getLongVolatile() { - UNSAFE.getLongVolatile(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1); - } + @FuzzTest + public void getIntVolatile_end(Boolean ignored) { + UNSAFE.getIntVolatile( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE); + } - static void getLongVolatile_end() { - UNSAFE.getLongVolatile( - new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE); - } + @FuzzTest + public void getLong(Boolean ignored) { + UNSAFE.getLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1); + } - static void getObject() { - UNSAFE.getObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1); - } + @FuzzTest + public void getLong_end(Boolean ignored) { + UNSAFE.getLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE); + } - static void getObject_end() { - UNSAFE.getObject( - new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE); - } + @FuzzTest + public void getLongVolatile(Boolean ignored) { + UNSAFE.getLongVolatile(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1); + } - static void getObjectVolatile() { - UNSAFE.getObjectVolatile(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1); - } + @FuzzTest + public void getLongVolatile_end(Boolean ignored) { + UNSAFE.getLongVolatile( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE); + } - static void getObjectVolatile_end() { - UNSAFE.getObjectVolatile( - new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE); - } + @FuzzTest + public void getObject(Boolean ignored) { + UNSAFE.getObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1); + } - static void getShort() { - UNSAFE.getShort(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET - 1); - } + @FuzzTest + public void getObject_end(Boolean ignored) { + UNSAFE.getObject( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE); + } - static void getShort_end() { - UNSAFE.getShort( - new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET + 5L * Unsafe.ARRAY_SHORT_INDEX_SCALE); - } + @FuzzTest + public void getObjectVolatile(Boolean ignored) { + UNSAFE.getObjectVolatile(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1); + } - static void getShortVolatile() { - UNSAFE.getShortVolatile(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET - 1); - } + @FuzzTest + public void getObjectVolatile_end(Boolean ignored) { + UNSAFE.getObjectVolatile( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE); + } - static void getShortVolatile_end() { - UNSAFE.getShortVolatile( - new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET + 5L * Unsafe.ARRAY_SHORT_INDEX_SCALE); - } + @FuzzTest + public void getShort(Boolean ignored) { + UNSAFE.getShort(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET - 1); + } - static void putBoolean() { - UNSAFE.putBoolean(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET - 1, true); - } + @FuzzTest + public void getShort_end(Boolean ignored) { + UNSAFE.getShort( + new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET + 5L * Unsafe.ARRAY_SHORT_INDEX_SCALE); + } - static void putBoolean_end() { - UNSAFE.putBoolean( - new boolean[5], - Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 5L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE, - true); - } + @FuzzTest + public void getShortVolatile(Boolean ignored) { + UNSAFE.getShortVolatile(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET - 1); + } - static void putBooleanVolatile() { - UNSAFE.putBooleanVolatile(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET - 1, true); - } + @FuzzTest + public void getShortVolatile_end(Boolean ignored) { + UNSAFE.getShortVolatile( + new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET + 5L * Unsafe.ARRAY_SHORT_INDEX_SCALE); + } - static void putBooleanVolatile_end() { - UNSAFE.putBooleanVolatile( - new boolean[5], - Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 5L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE, - true); - } + @FuzzTest + public void putBoolean(Boolean ignored) { + UNSAFE.putBoolean(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET - 1, true); + } - static void putByte() { - UNSAFE.putByte(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, (byte) 0); - } + @FuzzTest + public void putBoolean_end(Boolean ignored) { + UNSAFE.putBoolean( + new boolean[5], + Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 5L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE, + true); + } - static void putByte_end() { - UNSAFE.putByte( - new byte[5], - Unsafe.ARRAY_BYTE_BASE_OFFSET + 5L * Unsafe.ARRAY_BYTE_INDEX_SCALE, - (byte) 0); - } + @FuzzTest + public void putBooleanVolatile(Boolean ignored) { + UNSAFE.putBooleanVolatile(new boolean[5], Unsafe.ARRAY_BOOLEAN_BASE_OFFSET - 1, true); + } - static void putByteVolatile() { - UNSAFE.putByteVolatile(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, (byte) 0); - } + @FuzzTest + public void putBooleanVolatile_end(Boolean ignored) { + UNSAFE.putBooleanVolatile( + new boolean[5], + Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + 5L * Unsafe.ARRAY_BOOLEAN_INDEX_SCALE, + true); + } - static void putByteVolatile_end() { - UNSAFE.putByteVolatile( - new byte[5], - Unsafe.ARRAY_BYTE_BASE_OFFSET + 5L * Unsafe.ARRAY_BYTE_INDEX_SCALE, - (byte) 0); - } + @FuzzTest + public void putByte(Boolean ignored) { + UNSAFE.putByte(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, (byte) 0); + } - static void putChar() { - UNSAFE.putChar(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET - 1, 'a'); - } + @FuzzTest + public void putByte_end(Boolean ignored) { + UNSAFE.putByte( + new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET + 5L * Unsafe.ARRAY_BYTE_INDEX_SCALE, (byte) 0); + } - static void putChar_end() { - UNSAFE.putChar( - new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 5L * Unsafe.ARRAY_CHAR_INDEX_SCALE, 'a'); - } + @FuzzTest + public void putByteVolatile(Boolean ignored) { + UNSAFE.putByteVolatile(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, (byte) 0); + } - static void putCharVolatile() { - UNSAFE.putCharVolatile(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET - 1, 'a'); - } + @FuzzTest + public void putByteVolatile_end(Boolean ignored) { + UNSAFE.putByteVolatile( + new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET + 5L * Unsafe.ARRAY_BYTE_INDEX_SCALE, (byte) 0); + } - static void putCharVolatile_end() { - UNSAFE.putCharVolatile( - new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 5L * Unsafe.ARRAY_CHAR_INDEX_SCALE, 'a'); - } + @FuzzTest + public void putChar(Boolean ignored) { + UNSAFE.putChar(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET - 1, 'a'); + } - static void putDouble() { - UNSAFE.putDouble(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET - 1, 0); - } + @FuzzTest + public void putChar_end(Boolean ignored) { + UNSAFE.putChar( + new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 5L * Unsafe.ARRAY_CHAR_INDEX_SCALE, 'a'); + } - static void putDouble_end() { - UNSAFE.putDouble( - new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 5L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, 0); - } + @FuzzTest + public void putCharVolatile(Boolean ignored) { + UNSAFE.putCharVolatile(new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET - 1, 'a'); + } - static void putDoubleVolatile() { - UNSAFE.putDoubleVolatile(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET - 1, 0); - } + @FuzzTest + public void putCharVolatile_end(Boolean ignored) { + UNSAFE.putCharVolatile( + new char[5], Unsafe.ARRAY_CHAR_BASE_OFFSET + 5L * Unsafe.ARRAY_CHAR_INDEX_SCALE, 'a'); + } - static void putDoubleVolatile_end() { - UNSAFE.putDoubleVolatile( - new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 5L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, 0); - } + @FuzzTest + public void putDouble(Boolean ignored) { + UNSAFE.putDouble(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET - 1, 0); + } - static void putFloat() { - UNSAFE.putFloat(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET - 1, 0); - } + @FuzzTest + public void putDouble_end(Boolean ignored) { + UNSAFE.putDouble( + new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 5L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, 0); + } - static void putFloat_end() { - UNSAFE.putFloat( - new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 5L * Unsafe.ARRAY_FLOAT_INDEX_SCALE, 0); - } + @FuzzTest + public void putDoubleVolatile(Boolean ignored) { + UNSAFE.putDoubleVolatile(new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET - 1, 0); + } - static void putFloatVolatile() { - UNSAFE.putFloatVolatile(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET - 1, 0); - } + @FuzzTest + public void putDoubleVolatile_end(Boolean ignored) { + UNSAFE.putDoubleVolatile( + new double[5], Unsafe.ARRAY_DOUBLE_BASE_OFFSET + 5L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, 0); + } - static void putFloatVolatile_end() { - UNSAFE.putFloatVolatile( - new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 5L * Unsafe.ARRAY_FLOAT_INDEX_SCALE, 0); - } + @FuzzTest + public void putFloat(Boolean ignored) { + UNSAFE.putFloat(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET - 1, 0); + } - static void putInt() { - UNSAFE.putInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 0); - } + @FuzzTest + public void putFloat_end(Boolean ignored) { + UNSAFE.putFloat( + new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 5L * Unsafe.ARRAY_FLOAT_INDEX_SCALE, 0); + } - static void putInt_end() { - UNSAFE.putInt( - new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 0); - } + @FuzzTest + public void putFloatVolatile(Boolean ignored) { + UNSAFE.putFloatVolatile(new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET - 1, 0); + } - static void putIntVolatile() { - UNSAFE.putIntVolatile(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 0); - } + @FuzzTest + public void putFloatVolatile_end(Boolean ignored) { + UNSAFE.putFloatVolatile( + new float[5], Unsafe.ARRAY_FLOAT_BASE_OFFSET + 5L * Unsafe.ARRAY_FLOAT_INDEX_SCALE, 0); + } - static void putIntVolatile_end() { - UNSAFE.putIntVolatile( - new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 0); - } + @FuzzTest + public void putInt(Boolean ignored) { + UNSAFE.putInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 0); + } - static void putLong() { - UNSAFE.putLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 0); - } + @FuzzTest + public void putInt_end(Boolean ignored) { + UNSAFE.putInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 0); + } - static void putLong_end() { - UNSAFE.putLong( - new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0); - } + @FuzzTest + public void putIntVolatile(Boolean ignored) { + UNSAFE.putIntVolatile(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 0); + } - static void putLongVolatile() { - UNSAFE.putLongVolatile(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 0); - } + @FuzzTest + public void putIntVolatile_end(Boolean ignored) { + UNSAFE.putIntVolatile( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 0); + } - static void putLongVolatile_end() { - UNSAFE.putLongVolatile( - new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0); - } + @FuzzTest + public void putLong(Boolean ignored) { + UNSAFE.putLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 0); + } - static void putObject() { - UNSAFE.putObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, 0); - } + @FuzzTest + public void putLong_end(Boolean ignored) { + UNSAFE.putLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0); + } - static void putObject_end() { - UNSAFE.putObject( - new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, 0); - } + @FuzzTest + public void putLongVolatile(Boolean ignored) { + UNSAFE.putLongVolatile(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 0); + } - static void putObjectVolatile() { - UNSAFE.putObjectVolatile(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, 0); - } + @FuzzTest + public void putLongVolatile_end(Boolean ignored) { + UNSAFE.putLongVolatile( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0); + } - static void putObjectVolatile_end() { - UNSAFE.putObjectVolatile( - new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, 0); - } + @FuzzTest + public void putObject(Boolean ignored) { + UNSAFE.putObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, 0); + } - static void putOrderedInt() { - UNSAFE.putOrderedInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 0); - } + @FuzzTest + public void putObject_end(Boolean ignored) { + UNSAFE.putObject( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, 0); + } - static void putOrderedInt_end() { - UNSAFE.putOrderedInt( - new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 0); - } + @FuzzTest + public void putObjectVolatile(Boolean ignored) { + UNSAFE.putObjectVolatile(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, 0); + } - static void putOrderedLong() { - UNSAFE.putOrderedLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 0); - } + @FuzzTest + public void putObjectVolatile_end(Boolean ignored) { + UNSAFE.putObjectVolatile( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, 0); + } - static void putOrderedLong_end() { - UNSAFE.putOrderedLong( - new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0); - } + @FuzzTest + public void putOrderedInt(Boolean ignored) { + UNSAFE.putOrderedInt(new int[5], Unsafe.ARRAY_INT_BASE_OFFSET - 1, 0); + } - static void putOrderedObject() { - UNSAFE.putOrderedObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, 0); - } + @FuzzTest + public void putOrderedInt_end(Boolean ignored) { + UNSAFE.putOrderedInt( + new int[5], Unsafe.ARRAY_INT_BASE_OFFSET + 5L * Unsafe.ARRAY_INT_INDEX_SCALE, 0); + } - static void putOrderedObject_end() { - UNSAFE.putOrderedObject( - new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, 0); - } + @FuzzTest + public void putOrderedLong(Boolean ignored) { + UNSAFE.putOrderedLong(new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET - 1, 0); + } - static void putShort() { - UNSAFE.putShort(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET - 1, (short) 0); - } + @FuzzTest + public void putOrderedLong_end(Boolean ignored) { + UNSAFE.putOrderedLong( + new long[5], Unsafe.ARRAY_LONG_BASE_OFFSET + 5L * Unsafe.ARRAY_LONG_INDEX_SCALE, 0); + } - static void putShort_end() { - UNSAFE.putShort( - new short[5], - Unsafe.ARRAY_SHORT_BASE_OFFSET + 5L * Unsafe.ARRAY_SHORT_INDEX_SCALE, - (short) 0); - } + @FuzzTest + public void putOrderedObject(Boolean ignored) { + UNSAFE.putOrderedObject(new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET - 1, 0); + } - static void putShortVolatile() { - UNSAFE.putShortVolatile(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET - 1, (short) 0); - } + @FuzzTest + public void putOrderedObject_end(Boolean ignored) { + UNSAFE.putOrderedObject( + new Object[5], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 5L * Unsafe.ARRAY_OBJECT_INDEX_SCALE, 0); + } - static void putShortVolatile_end() { - UNSAFE.putShortVolatile( - new short[5], - Unsafe.ARRAY_SHORT_BASE_OFFSET + 5L * Unsafe.ARRAY_SHORT_INDEX_SCALE, - (short) 0); - } + @FuzzTest + public void putShort(Boolean ignored) { + UNSAFE.putShort(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET - 1, (short) 0); + } - static void copyMemory() { - UNSAFE.copyMemory( - new byte[5], - Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, - new byte[5], - Unsafe.ARRAY_BYTE_BASE_OFFSET, - 2); - } + @FuzzTest + public void putShort_end(Boolean ignored) { + UNSAFE.putShort( + new short[5], + Unsafe.ARRAY_SHORT_BASE_OFFSET + 5L * Unsafe.ARRAY_SHORT_INDEX_SCALE, + (short) 0); + } - static void copyMemory_end() { - UNSAFE.copyMemory( - new byte[5], - Unsafe.ARRAY_BYTE_BASE_OFFSET + 4L * Unsafe.ARRAY_BYTE_INDEX_SCALE, - new byte[5], - Unsafe.ARRAY_BYTE_BASE_OFFSET, - 2); - } + @FuzzTest + public void putShortVolatile(Boolean ignored) { + UNSAFE.putShortVolatile(new short[5], Unsafe.ARRAY_SHORT_BASE_OFFSET - 1, (short) 0); + } - static void copyMemory_dest() { - UNSAFE.copyMemory( - new byte[5], - Unsafe.ARRAY_BYTE_BASE_OFFSET, - new byte[5], - Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, - 2); - } + @FuzzTest + public void putShortVolatile_end(Boolean ignored) { + UNSAFE.putShortVolatile( + new short[5], + Unsafe.ARRAY_SHORT_BASE_OFFSET + 5L * Unsafe.ARRAY_SHORT_INDEX_SCALE, + (short) 0); + } - static void copyMemory_dest_end() { - UNSAFE.copyMemory( - new byte[5], - Unsafe.ARRAY_BYTE_BASE_OFFSET, - new byte[5], - Unsafe.ARRAY_BYTE_BASE_OFFSET + 4L * Unsafe.ARRAY_BYTE_INDEX_SCALE, - 2); - } + @FuzzTest + public void copyMemory(Boolean ignored) { + UNSAFE.copyMemory( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET, + 2); + } - static void setMemory() { - UNSAFE.setMemory(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, 2, (byte) 0); - } + @FuzzTest + public void copyMemory_end(Boolean ignored) { + UNSAFE.copyMemory( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET + 4L * Unsafe.ARRAY_BYTE_INDEX_SCALE, + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET, + 2); + } - static void setMemory_end() { - UNSAFE.setMemory( - new byte[5], - Unsafe.ARRAY_BYTE_BASE_OFFSET + 4L * Unsafe.ARRAY_BYTE_INDEX_SCALE, - 2, - (byte) 0); - } + @FuzzTest + public void copyMemory_dest(Boolean ignored) { + UNSAFE.copyMemory( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET, + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, + 2); + } - // The following covers some additional special cases of invalid memory access - static void byteAccessOnObjectArray() { - UNSAFE.getByte(new String[10], Unsafe.ARRAY_OBJECT_BASE_OFFSET); - } + @FuzzTest + public void copyMemory_dest_end(Boolean ignored) { + UNSAFE.copyMemory( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET, + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET + 4L * Unsafe.ARRAY_BYTE_INDEX_SCALE, + 2); + } - static void objectAccessOnPrimitiveArray() { - UNSAFE.getObject(new byte[100], Unsafe.ARRAY_BYTE_BASE_OFFSET); - } + @FuzzTest + public void setMemory(Boolean ignored) { + UNSAFE.setMemory(new byte[5], Unsafe.ARRAY_BYTE_BASE_OFFSET - 1, 2, (byte) 0); + } - static void unalignedObjectAccess() { - assert Unsafe.ARRAY_OBJECT_INDEX_SCALE != 1; - UNSAFE.getObject(new String[2], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 1); - } + @FuzzTest + public void setMemory_end(Boolean ignored) { + UNSAFE.setMemory( + new byte[5], + Unsafe.ARRAY_BYTE_BASE_OFFSET + 4L * Unsafe.ARRAY_BYTE_INDEX_SCALE, + 2, + (byte) 0); } - public static void fuzzerTestOneInput(FuzzedDataProvider data) throws Exception { - Method[] testMethods = TestMethods.class.getDeclaredMethods(); - // Since all of these methods are expected to cause a sanitizer exception, pick a random one and - // run it - // TODO: Is this a proper way to implement this? - Method testMethod = testMethods[data.consumeInt(0, testMethods.length - 1)]; + // The following covers some additional special cases of invalid memory access + @FuzzTest + public void byteAccessOnObjectArray(Boolean ignored) { + UNSAFE.getByte(new String[10], Unsafe.ARRAY_OBJECT_BASE_OFFSET); + } - testMethod.invoke(null); + @FuzzTest + public void objectAccessOnPrimitiveArray(Boolean ignored) { + UNSAFE.getObject(new byte[100], Unsafe.ARRAY_BYTE_BASE_OFFSET); + } - throw new AssertionError("No sanitizer exception was thrown for " + testMethod); + @FuzzTest + public void unalignedObjectAccess(Boolean ignored) { + assert Unsafe.ARRAY_OBJECT_INDEX_SCALE != 1; + UNSAFE.getObject(new String[2], Unsafe.ARRAY_OBJECT_BASE_OFFSET + 1); } } From c4d467cc8655ccf97845a2ced971b9b6366b0980 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Wed, 16 Jul 2025 00:17:35 +0200 Subject: [PATCH 7/9] Rename byte sized access check method --- .../jazzer/sanitizers/UnsafeSanitizer.java | 21 +++++++++---------- .../java/com/example/UnsafeArrayValid.java | 5 ++++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeSanitizer.java b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeSanitizer.java index 4cf22425d..3608df9b5 100644 --- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeSanitizer.java +++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/UnsafeSanitizer.java @@ -260,7 +260,7 @@ public static void objectAccessHook( public static void primitiveGetterHook( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { int accessSize = getBytesCount(method.type().returnType()); - checkPrimitiveAccess(arguments, accessSize); + checkByteSizedAccess(arguments, accessSize); } /** @@ -395,7 +395,7 @@ public static void primitiveSetterHook( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { int accessSize = getBytesCount(method.type().parameterType(2 + 1)); // + 1 for implicit Unsafe instance - checkPrimitiveAccess(arguments, accessSize); + checkByteSizedAccess(arguments, accessSize); } /** Hook for {@link Unsafe#setMemory(Object, long, long, byte)} */ @@ -407,7 +407,7 @@ public static void primitiveSetterHook( targetMethodDescriptor = "(Ljava/lang/Object;JJB)V") public static void setMemoryHook( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { - checkPrimitiveAccess(arguments[0], (long) arguments[1], (long) arguments[2]); + checkByteSizedAccess(arguments[0], (long) arguments[1], (long) arguments[2]); } /** Hook for {@link Unsafe#copyMemory(Object, long, Object, long, long)} */ @@ -420,8 +420,8 @@ public static void setMemoryHook( public static void copyMemoryHook( MethodHandle method, Object thisObject, Object[] arguments, int hookId) { long size = (long) arguments[4]; - checkPrimitiveAccess(arguments[0], (long) arguments[1], size); - checkPrimitiveAccess(arguments[2], (long) arguments[3], size); + checkByteSizedAccess(arguments[0], (long) arguments[1], size); + checkByteSizedAccess(arguments[2], (long) arguments[3], size); } private static void report(String message) { @@ -434,13 +434,13 @@ private static long offsetValue(Object obj) { return ((Number) obj).longValue(); } - private static void checkPrimitiveAccess(Object[] args, long accessSize) { + private static void checkByteSizedAccess(Object[] args, long accessSize) { Object obj = args[0]; long offset = offsetValue(args[1]); - checkPrimitiveAccess(obj, offset, accessSize); + checkByteSizedAccess(obj, offset, accessSize); } - private static void checkPrimitiveAccess(Object obj, long offset, long accessSize) { + private static void checkByteSizedAccess(Object obj, long offset, long accessSize) { checkAccess(obj, offset, accessSize, false); } @@ -482,11 +482,10 @@ private static void checkAccess( } // Mixing up bytes and object references (e.g. reading an object reference from a primitive - // array) - // seems error-prone and might mess with the garbage collector + // array) seems error-prone and might mess with the garbage collector if (isObjectAccess) { if (componentType.isPrimitive()) { - report("Reading or writing object reference from a " + objClass.getTypeName()); + report("Reading or writing an object reference from a " + objClass.getTypeName()); } } else { if (!componentType.isPrimitive()) { diff --git a/sanitizers/src/test/java/com/example/UnsafeArrayValid.java b/sanitizers/src/test/java/com/example/UnsafeArrayValid.java index 6be8f34da..47f73063b 100644 --- a/sanitizers/src/test/java/com/example/UnsafeArrayValid.java +++ b/sanitizers/src/test/java/com/example/UnsafeArrayValid.java @@ -19,7 +19,10 @@ import java.lang.reflect.Field; import sun.misc.Unsafe; -/** Verifies that valid {@link Unsafe} usage does not cause a spurious sanitizer exception. */ +/** + * Verifies that valid array access through {@link Unsafe} does not cause a spurious sanitizer + * exception. + */ public class UnsafeArrayValid { private static final Unsafe UNSAFE; From d0d36a0c696a1ddd36341c7788d94c25769b060b Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Wed, 16 Jul 2025 18:45:40 +0200 Subject: [PATCH 8/9] Fix BUILD.bazel formatting --- sanitizers/src/test/java/com/example/BUILD.bazel | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sanitizers/src/test/java/com/example/BUILD.bazel b/sanitizers/src/test/java/com/example/BUILD.bazel index a270d31c2..d7f4c9d6e 100644 --- a/sanitizers/src/test/java/com/example/BUILD.bazel +++ b/sanitizers/src/test/java/com/example/BUILD.bazel @@ -223,17 +223,17 @@ java_fuzz_target_test( srcs = [ "UnsafeArrayOutOfBounds.java", ], - fuzzer_args = [ - "-print_final_stats=1", - "-runs=0", + allowed_findings = [ + "com.code_intelligence.jazzer.api.FuzzerSecurityIssueCritical", ], env = { "JAZZER_FUZZ": "1", }, - allowed_findings = [ - "com.code_intelligence.jazzer.api.FuzzerSecurityIssueCritical", + expect_number_of_findings = 1, + fuzzer_args = [ + "-print_final_stats=1", + "-runs=0", ], - expect_number_of_findings=1, target_class = "com.example.UnsafeArrayOutOfBounds", target_method = method, verify_crash_reproducer = False, From 45405d2e476ebc35be4923d421ab968af016eedc Mon Sep 17 00:00:00 2001 From: Peter Samarin Date: Mon, 4 Aug 2025 17:52:52 +0200 Subject: [PATCH 9/9] TMP: replace FDP with new mutator framework --- .../java/com/example/OsCommandInjectionProcessBuilder.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sanitizers/src/test/java/com/example/OsCommandInjectionProcessBuilder.java b/sanitizers/src/test/java/com/example/OsCommandInjectionProcessBuilder.java index 2ff084798..acb0cd7df 100644 --- a/sanitizers/src/test/java/com/example/OsCommandInjectionProcessBuilder.java +++ b/sanitizers/src/test/java/com/example/OsCommandInjectionProcessBuilder.java @@ -16,12 +16,11 @@ package com.example; -import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.mutation.annotation.Ascii; import java.util.concurrent.TimeUnit; public class OsCommandInjectionProcessBuilder { - public static void fuzzerTestOneInput(FuzzedDataProvider data) { - String input = data.consumeRemainingAsAsciiString(); + public static void fuzzerTestOneInput(@Ascii String input) { try { ProcessBuilder processBuilder = new ProcessBuilder(input); processBuilder.environment().clear();