View Javadoc
1   /*
2    * Copyright 2013 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.util.internal;
17  
18  import io.netty.util.internal.logging.InternalLogger;
19  import io.netty.util.internal.logging.InternalLoggerFactory;
20  import sun.misc.Unsafe;
21  
22  import java.lang.invoke.MethodHandle;
23  import java.lang.invoke.MethodHandles;
24  import java.lang.reflect.Constructor;
25  import java.lang.reflect.Field;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  import java.nio.Buffer;
29  import java.nio.ByteBuffer;
30  import java.security.AccessController;
31  import java.security.PrivilegedAction;
32  import java.util.concurrent.atomic.AtomicLong;
33  
34  import static java.lang.invoke.MethodType.methodType;
35  
36  /**
37   * The {@link PlatformDependent} operations which requires access to {@code sun.misc.*}.
38   */
39  final class PlatformDependent0 {
40  
41      private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class);
42      private static final long ADDRESS_FIELD_OFFSET;
43      private static final long BYTE_ARRAY_BASE_OFFSET;
44      private static final long INT_ARRAY_BASE_OFFSET;
45      private static final long INT_ARRAY_INDEX_SCALE;
46      private static final long LONG_ARRAY_BASE_OFFSET;
47      private static final long LONG_ARRAY_INDEX_SCALE;
48      private static final MethodHandle DIRECT_BUFFER_CONSTRUCTOR;
49      private static final MethodHandle ALLOCATE_ARRAY_METHOD;
50      private static final MethodHandle ALIGN_SLICE;
51      private static final MethodHandle OFFSET_SLICE;
52      private static final boolean IS_ANDROID = isAndroid0();
53      private static final int JAVA_VERSION = javaVersion0();
54      private static final Throwable EXPLICIT_NO_UNSAFE_CAUSE = explicitNoUnsafeCause0();
55  
56      private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE;
57  
58      // See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/
59      // ImageInfo.java
60      private static final boolean RUNNING_IN_NATIVE_IMAGE = SystemPropertyUtil.contains(
61              "org.graalvm.nativeimage.imagecode");
62  
63      private static final boolean IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE = explicitTryReflectionSetAccessible0();
64  
65      // Package-private for testing.
66      static final MethodHandle IS_VIRTUAL_THREAD_METHOD_HANDLE = getIsVirtualThreadMethodHandle();
67  
68      static final Unsafe UNSAFE;
69  
70      // constants borrowed from murmur3
71      static final int HASH_CODE_ASCII_SEED = 0xc2b2ae35;
72      static final int HASH_CODE_C1 = 0xcc9e2d51;
73      static final int HASH_CODE_C2 = 0x1b873593;
74  
75      /**
76       * Limits the number of bytes to copy per {@link Unsafe#copyMemory(long, long, long)} to allow safepoint polling
77       * during a large copy.
78       */
79      private static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;
80  
81      private static final boolean UNALIGNED;
82  
83      private static final long BITS_MAX_DIRECT_MEMORY;
84  
85      static {
86          MethodHandles.Lookup lookup = MethodHandles.lookup();
87          final ByteBuffer direct;
88          Field addressField = null;
89          MethodHandle allocateArrayMethod = null;
90          Throwable unsafeUnavailabilityCause;
91          Unsafe unsafe;
92          if ((unsafeUnavailabilityCause = EXPLICIT_NO_UNSAFE_CAUSE) != null) {
93              direct = null;
94              addressField = null;
95              unsafe = null;
96          } else {
97              direct = ByteBuffer.allocateDirect(1);
98  
99              // attempt to access field Unsafe#theUnsafe
100             final Object maybeUnsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() {
101                 @Override
102                 public Object run() {
103                     try {
104                         final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
105                         // We always want to try using Unsafe as the access still works on java9 as well and
106                         // we need it for out native-transports and many optimizations.
107                         Throwable cause = ReflectionUtil.trySetAccessible(unsafeField, false);
108                         if (cause != null) {
109                             return cause;
110                         }
111                         // the unsafe instance
112                         return unsafeField.get(null);
113                     } catch (NoSuchFieldException | IllegalAccessException | SecurityException e) {
114                         return e;
115                     } catch (NoClassDefFoundError e) {
116                         // Also catch NoClassDefFoundError in case someone uses for example OSGI and it made
117                         // Unsafe unloadable.
118                         return e;
119                     }
120                 }
121             });
122 
123             // the conditional check here can not be replaced with checking that maybeUnsafe
124             // is an instanceof Unsafe and reversing the if and else blocks; this is because an
125             // instanceof check against Unsafe will trigger a class load and we might not have
126             // the runtime permission accessClassInPackage.sun.misc
127             if (maybeUnsafe instanceof Throwable) {
128                 unsafe = null;
129                 unsafeUnavailabilityCause = (Throwable) maybeUnsafe;
130                 if (logger.isTraceEnabled()) {
131                     logger.debug("sun.misc.Unsafe.theUnsafe: unavailable", unsafeUnavailabilityCause);
132                 } else {
133                     logger.debug("sun.misc.Unsafe.theUnsafe: unavailable: {}", unsafeUnavailabilityCause.getMessage());
134                 }
135             } else {
136                 unsafe = (Unsafe) maybeUnsafe;
137                 logger.debug("sun.misc.Unsafe.theUnsafe: available");
138             }
139 
140             // ensure the unsafe supports all necessary methods to work around the mistake in the latest OpenJDK,
141             // or that they haven't been removed by JEP 471.
142             // https://github.com/netty/netty/issues/1061
143             // https://www.mail-archive.com/[email protected]/msg00698.html
144             // https://openjdk.org/jeps/471
145             if (unsafe != null) {
146                 final Unsafe finalUnsafe = unsafe;
147                 final Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
148                     @Override
149                     public Object run() {
150                         try {
151                             // Other methods like storeFence() and invokeCleaner() are tested for elsewhere.
152                             Class<? extends Unsafe> cls = finalUnsafe.getClass();
153                             cls.getDeclaredMethod(
154                                     "copyMemory", Object.class, long.class, Object.class, long.class, long.class);
155                             if (javaVersion() > 23) {
156                                 cls.getDeclaredMethod("objectFieldOffset", Field.class);
157                                 cls.getDeclaredMethod("staticFieldOffset", Field.class);
158                                 cls.getDeclaredMethod("staticFieldBase", Field.class);
159                                 cls.getDeclaredMethod("arrayBaseOffset", Class.class);
160                                 cls.getDeclaredMethod("arrayIndexScale", Class.class);
161                                 cls.getDeclaredMethod("allocateMemory", long.class);
162                                 cls.getDeclaredMethod("reallocateMemory", long.class, long.class);
163                                 cls.getDeclaredMethod("freeMemory", long.class);
164                                 cls.getDeclaredMethod("setMemory", long.class, long.class, byte.class);
165                                 cls.getDeclaredMethod("setMemory", Object.class, long.class, long.class, byte.class);
166                                 cls.getDeclaredMethod("getBoolean", Object.class, long.class);
167                                 cls.getDeclaredMethod("getByte", long.class);
168                                 cls.getDeclaredMethod("getByte", Object.class, long.class);
169                                 cls.getDeclaredMethod("getInt", long.class);
170                                 cls.getDeclaredMethod("getInt", Object.class, long.class);
171                                 cls.getDeclaredMethod("getLong", long.class);
172                                 cls.getDeclaredMethod("getLong", Object.class, long.class);
173                                 cls.getDeclaredMethod("putByte", long.class, byte.class);
174                                 cls.getDeclaredMethod("putByte", Object.class, long.class, byte.class);
175                                 cls.getDeclaredMethod("putInt", long.class, int.class);
176                                 cls.getDeclaredMethod("putInt", Object.class, long.class, int.class);
177                                 cls.getDeclaredMethod("putLong", long.class, long.class);
178                                 cls.getDeclaredMethod("putLong", Object.class, long.class, long.class);
179                                 cls.getDeclaredMethod("addressSize");
180                             }
181                             if (javaVersion() >= 23) {
182                                 // The following tests the methods are usable.
183                                 // Will throw UnsupportedOperationException if unsafe memory access is denied:
184                                 long address = finalUnsafe.allocateMemory(8);
185                                 finalUnsafe.putLong(address, 42);
186                                 finalUnsafe.freeMemory(address);
187                             }
188                             return null;
189                         } catch (UnsupportedOperationException | SecurityException | NoSuchMethodException e) {
190                             return e;
191                         }
192                     }
193                 });
194 
195                 if (maybeException == null) {
196                     logger.debug("sun.misc.Unsafe base methods: all available");
197                 } else {
198                     // Unsafe.copyMemory(Object, long, Object, long, long) unavailable.
199                     unsafe = null;
200                     unsafeUnavailabilityCause = (Throwable) maybeException;
201                     if (logger.isTraceEnabled()) {
202                         logger.debug("sun.misc.Unsafe method unavailable:", unsafeUnavailabilityCause);
203                     } else {
204                         logger.debug("sun.misc.Unsafe method unavailable: {}", unsafeUnavailabilityCause.getMessage());
205                     }
206                 }
207             }
208 
209             if (unsafe != null) {
210                 final Unsafe finalUnsafe = unsafe;
211 
212                 // attempt to access field Buffer#address
213                 final Object maybeAddressField = AccessController.doPrivileged(new PrivilegedAction<Object>() {
214                     @Override
215                     public Object run() {
216                         try {
217                             final Field field = Buffer.class.getDeclaredField("address");
218                             // Use Unsafe to read value of the address field. This way it will not fail on JDK9+ which
219                             // will forbid changing the access level via reflection.
220                             final long offset = finalUnsafe.objectFieldOffset(field);
221                             final long address = finalUnsafe.getLong(direct, offset);
222 
223                             // if direct really is a direct buffer, address will be non-zero
224                             if (address == 0) {
225                                 return null;
226                             }
227                             return field;
228                         } catch (NoSuchFieldException | SecurityException e) {
229                             return e;
230                         }
231                     }
232                 });
233 
234                 if (maybeAddressField instanceof Field) {
235                     addressField = (Field) maybeAddressField;
236                     logger.debug("java.nio.Buffer.address: available");
237                 } else {
238                     unsafeUnavailabilityCause = (Throwable) maybeAddressField;
239                     if (logger.isTraceEnabled()) {
240                         logger.debug("java.nio.Buffer.address: unavailable", (Throwable) maybeAddressField);
241                     } else {
242                         logger.debug("java.nio.Buffer.address: unavailable: {}",
243                                 ((Throwable) maybeAddressField).getMessage());
244                     }
245 
246                     // If we cannot access the address of a direct buffer, there's no point of using unsafe.
247                     // Let's just pretend unsafe is unavailable for overall simplicity.
248                     unsafe = null;
249                 }
250             }
251 
252             if (unsafe != null) {
253                 // There are assumptions made where ever BYTE_ARRAY_BASE_OFFSET is used (equals, hashCodeAscii, and
254                 // primitive accessors) that arrayIndexScale == 1, and results are undefined if this is not the case.
255                 long byteArrayIndexScale = unsafe.arrayIndexScale(byte[].class);
256                 if (byteArrayIndexScale != 1) {
257                     logger.debug("unsafe.arrayIndexScale is {} (expected: 1). Not using unsafe.", byteArrayIndexScale);
258                     unsafeUnavailabilityCause = new UnsupportedOperationException("Unexpected unsafe.arrayIndexScale");
259                     unsafe = null;
260                 }
261             }
262         }
263         UNSAFE_UNAVAILABILITY_CAUSE = unsafeUnavailabilityCause;
264         UNSAFE = unsafe;
265 
266         if (unsafe == null) {
267             ADDRESS_FIELD_OFFSET = -1;
268             BYTE_ARRAY_BASE_OFFSET = -1;
269             LONG_ARRAY_BASE_OFFSET = -1;
270             LONG_ARRAY_INDEX_SCALE = -1;
271             INT_ARRAY_BASE_OFFSET = -1;
272             INT_ARRAY_INDEX_SCALE = -1;
273             UNALIGNED = false;
274             BITS_MAX_DIRECT_MEMORY = -1;
275             DIRECT_BUFFER_CONSTRUCTOR = null;
276             ALLOCATE_ARRAY_METHOD = null;
277         } else {
278             MethodHandle directBufferConstructor;
279             long address = -1;
280             try {
281                 final Object maybeDirectBufferConstructor =
282                         AccessController.doPrivileged(new PrivilegedAction<Object>() {
283                             @Override
284                             public Object run() {
285                                 try {
286                                     Class<? extends ByteBuffer> directClass = direct.getClass();
287                                     final Constructor<?> constructor = javaVersion() >= 21 ?
288                                             directClass.getDeclaredConstructor(long.class, long.class) :
289                                             directClass.getDeclaredConstructor(long.class, int.class);
290                                     Throwable cause = ReflectionUtil.trySetAccessible(constructor, true);
291                                     if (cause != null) {
292                                         return cause;
293                                     }
294                                     return lookup.unreflectConstructor(constructor)
295                                             .asType(methodType(ByteBuffer.class, long.class, int.class));
296                                 } catch (Throwable e) {
297                                     return e;
298                                 }
299                             }
300                         });
301 
302                 if (maybeDirectBufferConstructor instanceof MethodHandle) {
303                     address = UNSAFE.allocateMemory(1);
304                     // try to use the constructor now
305                     try {
306                         MethodHandle constructor = (MethodHandle) maybeDirectBufferConstructor;
307                         ByteBuffer ignore = (ByteBuffer) constructor.invokeExact(address, 1);
308                         directBufferConstructor = constructor;
309                         logger.debug("direct buffer constructor: available");
310                     } catch (Throwable e) {
311                         directBufferConstructor = null;
312                     }
313                 } else {
314                     if (logger.isTraceEnabled()) {
315                         logger.debug("direct buffer constructor: unavailable",
316                                 (Throwable) maybeDirectBufferConstructor);
317                     } else {
318                         logger.debug("direct buffer constructor: unavailable: {}",
319                                 ((Throwable) maybeDirectBufferConstructor).getMessage());
320                     }
321                     directBufferConstructor = null;
322                 }
323             } finally {
324                 if (address != -1) {
325                     UNSAFE.freeMemory(address);
326                 }
327             }
328             DIRECT_BUFFER_CONSTRUCTOR = directBufferConstructor;
329             ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
330             BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
331             INT_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(int[].class);
332             INT_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(int[].class);
333             LONG_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(long[].class);
334             LONG_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(long[].class);
335             final boolean unaligned;
336             // using a known type to avoid loading new classes
337             final AtomicLong maybeMaxMemory = new AtomicLong(-1);
338             Object maybeUnaligned = AccessController.doPrivileged(new PrivilegedAction<Object>() {
339                 @Override
340                 public Object run() {
341                     try {
342                         Class<?> bitsClass =
343                                 Class.forName("java.nio.Bits", false, getSystemClassLoader());
344                         int version = javaVersion();
345                         if (!isNativeImage() && version >= 9) {
346                             // Java9/10 use all lowercase and later versions all uppercase.
347                             String fieldName = version >= 11? "MAX_MEMORY" : "maxMemory";
348                             // On Java9 and later we try to directly access the field as we can do this without
349                             // adjust the accessible levels.
350                             try {
351                                 Field maxMemoryField = bitsClass.getDeclaredField(fieldName);
352                                 if (maxMemoryField.getType() == long.class) {
353                                     long offset = UNSAFE.staticFieldOffset(maxMemoryField);
354                                     Object object = UNSAFE.staticFieldBase(maxMemoryField);
355                                     maybeMaxMemory.lazySet(UNSAFE.getLong(object, offset));
356                                 }
357                             } catch (Throwable ignore) {
358                                 // ignore if can't access
359                             }
360                             fieldName = version >= 11? "UNALIGNED" : "unaligned";
361                             try {
362                                 Field unalignedField = bitsClass.getDeclaredField(fieldName);
363                                 if (unalignedField.getType() == boolean.class) {
364                                     long offset = UNSAFE.staticFieldOffset(unalignedField);
365                                     Object object = UNSAFE.staticFieldBase(unalignedField);
366                                     return UNSAFE.getBoolean(object, offset);
367                                 }
368                                 // There is something unexpected stored in the field,
369                                 // let us fall-back and try to use a reflective method call as last resort.
370                             } catch (NoSuchFieldException ignore) {
371                                 // We did not find the field we expected, move on.
372                             }
373                         }
374                         Method unalignedMethod = bitsClass.getDeclaredMethod("unaligned");
375                         Throwable cause = ReflectionUtil.trySetAccessible(unalignedMethod, true);
376                         if (cause != null) {
377                             return cause;
378                         }
379                         return unalignedMethod.invoke(null);
380                     } catch (NoSuchMethodException | SecurityException | IllegalAccessException |
381                              InvocationTargetException | ClassNotFoundException e) {
382                         return e;
383                     }
384                 }
385             });
386 
387             if (maybeUnaligned instanceof Boolean) {
388                 unaligned = (Boolean) maybeUnaligned;
389                 logger.debug("java.nio.Bits.unaligned: available, {}", unaligned);
390             } else {
391                 String arch = SystemPropertyUtil.get("os.arch", "");
392                 //noinspection DynamicRegexReplaceableByCompiledPattern
393                 unaligned = arch.matches("^(i[3-6]86|x86(_64)?|x64|amd64)$");
394                 Throwable t = (Throwable) maybeUnaligned;
395                 if (logger.isTraceEnabled()) {
396                     logger.debug("java.nio.Bits.unaligned: unavailable, {}", unaligned, t);
397                 } else {
398                     logger.debug("java.nio.Bits.unaligned: unavailable, {}, {}", unaligned, t.getMessage());
399                 }
400             }
401 
402             UNALIGNED = unaligned;
403             BITS_MAX_DIRECT_MEMORY = maybeMaxMemory.get() >= 0? maybeMaxMemory.get() : -1;
404 
405             if (javaVersion() >= 9) {
406                 Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
407                     @Override
408                     public Object run() {
409                         try {
410                             // Java9 has jdk.internal.misc.Unsafe and not all methods are propagated to
411                             // sun.misc.Unsafe
412                             Class<?> cls = getClassLoader(PlatformDependent0.class)
413                                     .loadClass("jdk.internal.misc.Unsafe");
414                             return lookup.findStatic(cls, "getUnsafe", methodType(cls)).invoke();
415                         } catch (Throwable e) {
416                             return e;
417                         }
418                     }
419                 });
420                 if (!(maybeException instanceof Throwable)) {
421                     final Object finalInternalUnsafe = maybeException;
422                     maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
423                         @Override
424                         public Object run() {
425                             try {
426                                 Class<?> finalInternalUnsafeClass = finalInternalUnsafe.getClass();
427                                 return lookup.findVirtual(
428                                         finalInternalUnsafeClass,
429                                         "allocateUninitializedArray",
430                                         methodType(Object.class, Class.class, int.class));
431                             } catch (Throwable e) {
432                                 return e;
433                             }
434                         }
435                     });
436 
437                     if (maybeException instanceof MethodHandle) {
438                         try {
439                             MethodHandle m = (MethodHandle) maybeException;
440                             m = m.bindTo(finalInternalUnsafe);
441                             byte[] bytes = (byte[]) (Object) m.invokeExact(byte.class, 8);
442                             assert bytes.length == 8;
443                             allocateArrayMethod = m;
444                         } catch (Throwable e) {
445                             maybeException = e;
446                         }
447                     }
448                 }
449 
450                 if (maybeException instanceof Throwable) {
451                     if (logger.isTraceEnabled()) {
452                         logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable",
453                                 (Throwable) maybeException);
454                     } else {
455                         logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable: {}",
456                                 ((Throwable) maybeException).getMessage());
457                     }
458                 } else {
459                     logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): available");
460                 }
461             } else {
462                 logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9");
463             }
464             ALLOCATE_ARRAY_METHOD = allocateArrayMethod;
465         }
466 
467         if (javaVersion() > 9) {
468             ALIGN_SLICE = (MethodHandle) AccessController.doPrivileged(new PrivilegedAction<Object>() {
469                 @Override
470                 public Object run() {
471                     try {
472                         return MethodHandles.publicLookup().findVirtual(
473                                 ByteBuffer.class, "alignedSlice", methodType(ByteBuffer.class, int.class));
474                     } catch (Throwable e) {
475                         return null;
476                     }
477                 }
478             });
479         } else {
480             ALIGN_SLICE = null;
481         }
482 
483         if (javaVersion() >= 13) {
484             OFFSET_SLICE = (MethodHandle) AccessController.doPrivileged(new PrivilegedAction<Object>() {
485                 @Override
486                 public Object run() {
487                     try {
488                         return MethodHandles.publicLookup().findVirtual(
489                                 ByteBuffer.class, "slice", methodType(ByteBuffer.class, int.class, int.class));
490                     } catch (Throwable e) {
491                         return null;
492                     }
493                 }
494             });
495         } else {
496             OFFSET_SLICE = null;
497         }
498 
499         logger.debug("java.nio.DirectByteBuffer.<init>(long, {int,long}): {}",
500                 DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable");
501     }
502 
503     private static MethodHandle getIsVirtualThreadMethodHandle() {
504         try {
505             MethodHandle methodHandle = MethodHandles.publicLookup().findVirtual(Thread.class, "isVirtual",
506                     methodType(boolean.class));
507             // Call once to make sure the invocation works.
508             boolean isVirtual = (boolean) methodHandle.invokeExact(Thread.currentThread());
509             return methodHandle;
510         } catch (Throwable e) {
511             if (logger.isTraceEnabled()) {
512                 logger.debug("Thread.isVirtual() is not available: ", e);
513             } else {
514                 logger.debug("Thread.isVirtual() is not available: ", e.getMessage());
515             }
516             return null;
517         }
518     }
519 
520     /**
521      * @param thread The thread to be checked.
522      * @return {@code true} if this {@link Thread} is a virtual thread, {@code false} otherwise.
523      */
524     static boolean isVirtualThread(Thread thread) {
525         if (thread == null || IS_VIRTUAL_THREAD_METHOD_HANDLE == null) {
526             return false;
527         }
528         try {
529             return (boolean) IS_VIRTUAL_THREAD_METHOD_HANDLE.invokeExact(thread);
530         } catch (Throwable t) {
531             // Should not happen.
532             if (t instanceof Error) {
533                 throw (Error) t;
534             }
535             throw new Error(t);
536         }
537     }
538 
539     static boolean isNativeImage() {
540         return RUNNING_IN_NATIVE_IMAGE;
541     }
542 
543     static boolean isExplicitNoUnsafe() {
544         return EXPLICIT_NO_UNSAFE_CAUSE != null;
545     }
546 
547     private static Throwable explicitNoUnsafeCause0() {
548         boolean explicitProperty = SystemPropertyUtil.contains("io.netty.noUnsafe");
549         boolean noUnsafe = SystemPropertyUtil.getBoolean("io.netty.noUnsafe", false);
550         logger.debug("-Dio.netty.noUnsafe: {}", noUnsafe);
551 
552         // See JDK 23 JEP 471 https://openjdk.org/jeps/471 and sun.misc.Unsafe.beforeMemoryAccess() on JDK 23+.
553         // And JDK 24 JEP 498 https://openjdk.org/jeps/498, that enable warnings by default.
554         // Due to JDK bugs, we only actually disable Unsafe by default on Java 25+, where we have memory segment APIs
555         // available, and working.
556         String reason = "io.netty.noUnsafe";
557         String unspecified = "<unspecified>";
558         String unsafeMemoryAccess = SystemPropertyUtil.get("sun.misc.unsafe.memory.access", unspecified);
559         if (!explicitProperty && unspecified.equals(unsafeMemoryAccess) && javaVersion() >= 25) {
560             reason = "io.netty.noUnsafe=true by default on Java 25+";
561             noUnsafe = true;
562         } else if (!("allow".equals(unsafeMemoryAccess) || unspecified.equals(unsafeMemoryAccess))) {
563             reason = "--sun-misc-unsafe-memory-access=" + unsafeMemoryAccess;
564             noUnsafe = true;
565         }
566 
567         if (noUnsafe) {
568             String msg = "sun.misc.Unsafe: unavailable (" + reason + ')';
569             logger.debug(msg);
570             return new UnsupportedOperationException(msg);
571         }
572 
573         // Legacy properties
574         String unsafePropName;
575         if (SystemPropertyUtil.contains("io.netty.tryUnsafe")) {
576             unsafePropName = "io.netty.tryUnsafe";
577         } else {
578             unsafePropName = "org.jboss.netty.tryUnsafe";
579         }
580 
581         if (!SystemPropertyUtil.getBoolean(unsafePropName, true)) {
582             String msg = "sun.misc.Unsafe: unavailable (" + unsafePropName + ')';
583             logger.debug(msg);
584             return new UnsupportedOperationException(msg);
585         }
586 
587         return null;
588     }
589 
590     static boolean isUnaligned() {
591         return UNALIGNED;
592     }
593 
594     /**
595      * Any value >= 0 should be considered as a valid max direct memory value.
596      */
597     static long bitsMaxDirectMemory() {
598         return BITS_MAX_DIRECT_MEMORY;
599     }
600 
601     static boolean hasUnsafe() {
602         return UNSAFE != null;
603     }
604 
605     static Throwable getUnsafeUnavailabilityCause() {
606         return UNSAFE_UNAVAILABILITY_CAUSE;
607     }
608 
609     static boolean unalignedAccess() {
610         return UNALIGNED;
611     }
612 
613     static void throwException(Throwable cause) {
614         throwException0(cause);
615     }
616 
617     @SuppressWarnings("unchecked")
618     private static <E extends Throwable> void throwException0(Throwable t) throws E {
619         throw (E) t;
620     }
621 
622     static boolean hasDirectBufferNoCleanerConstructor() {
623         return DIRECT_BUFFER_CONSTRUCTOR != null;
624     }
625 
626     static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) {
627         return newDirectBuffer(UNSAFE.reallocateMemory(directBufferAddress(buffer), capacity), capacity);
628     }
629 
630     static ByteBuffer allocateDirectNoCleaner(int capacity) {
631         // Calling malloc with capacity of 0 may return a null ptr or a memory address that can be used.
632         // Just use 1 to make it safe to use in all cases:
633         // See: https://pubs.opengroup.org/onlinepubs/009695399/functions/malloc.html
634         return newDirectBuffer(UNSAFE.allocateMemory(Math.max(1, capacity)), capacity);
635     }
636 
637     static boolean hasAlignSliceMethod() {
638         return ALIGN_SLICE != null;
639     }
640 
641     static ByteBuffer alignSlice(ByteBuffer buffer, int alignment) {
642         try {
643             return (ByteBuffer) ALIGN_SLICE.invokeExact(buffer, alignment);
644         } catch (Throwable e) {
645             rethrowIfPossible(e);
646             throw new LinkageError("ByteBuffer.alignedSlice not available", e);
647         }
648     }
649 
650     static boolean hasOffsetSliceMethod() {
651         return OFFSET_SLICE != null;
652     }
653 
654     static ByteBuffer offsetSlice(ByteBuffer buffer, int index, int length) {
655         try {
656             return (ByteBuffer) OFFSET_SLICE.invokeExact(buffer, index, length);
657         } catch (Throwable e) {
658             rethrowIfPossible(e);
659             throw new LinkageError("ByteBuffer.slice(int, int) not available", e);
660         }
661     }
662 
663     static boolean hasAllocateArrayMethod() {
664         return ALLOCATE_ARRAY_METHOD != null;
665     }
666 
667     static byte[] allocateUninitializedArray(int size) {
668         try {
669             return (byte[]) (Object) ALLOCATE_ARRAY_METHOD.invokeExact(byte.class, size);
670         } catch (Throwable e) {
671             rethrowIfPossible(e);
672             throw new LinkageError("Unsafe.allocateUninitializedArray not available", e);
673         }
674     }
675 
676     static ByteBuffer newDirectBuffer(long address, int capacity) {
677         ObjectUtil.checkPositiveOrZero(capacity, "capacity");
678 
679         try {
680             return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.invokeExact(address, capacity);
681         } catch (Throwable cause) {
682             rethrowIfPossible(cause);
683             throw new LinkageError("DirectByteBuffer constructor not available", cause);
684         }
685     }
686 
687     private static void rethrowIfPossible(Throwable cause) {
688         if (cause instanceof Error) {
689             throw (Error) cause;
690         }
691         if (cause instanceof RuntimeException) {
692             throw (RuntimeException) cause;
693         }
694     }
695 
696     static long directBufferAddress(ByteBuffer buffer) {
697         return getLong(buffer, ADDRESS_FIELD_OFFSET);
698     }
699 
700     static long byteArrayBaseOffset() {
701         return BYTE_ARRAY_BASE_OFFSET;
702     }
703 
704     static Object getObject(Object object, long fieldOffset) {
705         return UNSAFE.getObject(object, fieldOffset);
706     }
707 
708     static int getInt(Object object, long fieldOffset) {
709         return UNSAFE.getInt(object, fieldOffset);
710     }
711 
712     static int getIntVolatile(Object object, long fieldOffset) {
713         return UNSAFE.getIntVolatile(object, fieldOffset);
714     }
715 
716     static void putOrderedInt(Object object, long fieldOffset, int value) {
717         UNSAFE.putOrderedInt(object, fieldOffset, value);
718     }
719 
720     static int getAndAddInt(Object object, long fieldOffset, int value) {
721         return UNSAFE.getAndAddInt(object, fieldOffset, value);
722     }
723 
724     static boolean compareAndSwapInt(Object object, long fieldOffset, int expected, int value) {
725         return UNSAFE.compareAndSwapInt(object, fieldOffset, expected, value);
726     }
727 
728     static void safeConstructPutInt(Object object, long fieldOffset, int value) {
729         UNSAFE.putInt(object, fieldOffset, value);
730         UNSAFE.storeFence();
731     }
732 
733     private static long getLong(Object object, long fieldOffset) {
734         return UNSAFE.getLong(object, fieldOffset);
735     }
736 
737     static long objectFieldOffset(Field field) {
738         return UNSAFE.objectFieldOffset(field);
739     }
740 
741     static byte getByte(long address) {
742         return UNSAFE.getByte(address);
743     }
744 
745     static short getShort(long address) {
746         return UNSAFE.getShort(address);
747     }
748 
749     static int getInt(long address) {
750         return UNSAFE.getInt(address);
751     }
752 
753     static long getLong(long address) {
754         return UNSAFE.getLong(address);
755     }
756 
757     static byte getByte(byte[] data, int index) {
758         return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
759     }
760 
761     static byte getByte(byte[] data, long index) {
762         return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
763     }
764 
765     static short getShort(byte[] data, int index) {
766         return UNSAFE.getShort(data, BYTE_ARRAY_BASE_OFFSET + index);
767     }
768 
769     static int getInt(byte[] data, int index) {
770         return UNSAFE.getInt(data, BYTE_ARRAY_BASE_OFFSET + index);
771     }
772 
773     static int getInt(int[] data, long index) {
774         return UNSAFE.getInt(data, INT_ARRAY_BASE_OFFSET + INT_ARRAY_INDEX_SCALE * index);
775     }
776 
777     static long getLong(byte[] data, int index) {
778         return UNSAFE.getLong(data, BYTE_ARRAY_BASE_OFFSET + index);
779     }
780 
781     static long getLong(long[] data, long index) {
782         return UNSAFE.getLong(data, LONG_ARRAY_BASE_OFFSET + LONG_ARRAY_INDEX_SCALE * index);
783     }
784 
785     static void putByte(long address, byte value) {
786         UNSAFE.putByte(address, value);
787     }
788 
789     static void putShort(long address, short value) {
790         UNSAFE.putShort(address, value);
791     }
792 
793     static void putShortOrdered(long address, short newValue) {
794         UNSAFE.storeFence();
795         UNSAFE.putShort(null, address, newValue);
796     }
797 
798     static void putInt(long address, int value) {
799         UNSAFE.putInt(address, value);
800     }
801 
802     static void putLong(long address, long value) {
803         UNSAFE.putLong(address, value);
804     }
805 
806     static void putByte(byte[] data, int index, byte value) {
807         UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value);
808     }
809 
810     static void putByte(Object data, long offset, byte value) {
811         UNSAFE.putByte(data, offset, value);
812     }
813 
814     static void putShort(byte[] data, int index, short value) {
815         UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value);
816     }
817 
818     static void putInt(byte[] data, int index, int value) {
819         UNSAFE.putInt(data, BYTE_ARRAY_BASE_OFFSET + index, value);
820     }
821 
822     static void putLong(byte[] data, int index, long value) {
823         UNSAFE.putLong(data, BYTE_ARRAY_BASE_OFFSET + index, value);
824     }
825 
826     static void putObject(Object o, long offset, Object x) {
827         UNSAFE.putObject(o, offset, x);
828     }
829 
830     static void copyMemory(long srcAddr, long dstAddr, long length) {
831         // Manual safe-point polling is only needed prior Java9:
832         // See https://bugs.openjdk.java.net/browse/JDK-8149596
833         if (javaVersion() <= 8) {
834             copyMemoryWithSafePointPolling(srcAddr, dstAddr, length);
835         } else {
836             UNSAFE.copyMemory(srcAddr, dstAddr, length);
837         }
838     }
839 
840     private static void copyMemoryWithSafePointPolling(long srcAddr, long dstAddr, long length) {
841         while (length > 0) {
842             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
843             UNSAFE.copyMemory(srcAddr, dstAddr, size);
844             length -= size;
845             srcAddr += size;
846             dstAddr += size;
847         }
848     }
849 
850     static void copyMemory(Object src, long srcOffset, Object dst, long dstOffset, long length) {
851         // Manual safe-point polling is only needed prior Java9:
852         // See https://bugs.openjdk.java.net/browse/JDK-8149596
853         if (javaVersion() <= 8) {
854             copyMemoryWithSafePointPolling(src, srcOffset, dst, dstOffset, length);
855         } else {
856             UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, length);
857         }
858     }
859 
860     private static void copyMemoryWithSafePointPolling(
861             Object src, long srcOffset, Object dst, long dstOffset, long length) {
862         while (length > 0) {
863             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
864             UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size);
865             length -= size;
866             srcOffset += size;
867             dstOffset += size;
868         }
869     }
870 
871     static void setMemory(long address, long bytes, byte value) {
872         UNSAFE.setMemory(address, bytes, value);
873     }
874 
875     static void setMemory(Object o, long offset, long bytes, byte value) {
876         UNSAFE.setMemory(o, offset, bytes, value);
877     }
878 
879     static boolean equals(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
880         int remainingBytes = length & 7;
881         final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1;
882         final long diff = startPos2 - startPos1;
883         if (length >= 8) {
884             final long end = baseOffset1 + remainingBytes;
885             for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) {
886                 if (UNSAFE.getLong(bytes1, i) != UNSAFE.getLong(bytes2, i + diff)) {
887                     return false;
888                 }
889             }
890         }
891         if (remainingBytes >= 4) {
892             remainingBytes -= 4;
893             long pos = baseOffset1 + remainingBytes;
894             if (UNSAFE.getInt(bytes1, pos) != UNSAFE.getInt(bytes2, pos + diff)) {
895                 return false;
896             }
897         }
898         final long baseOffset2 = baseOffset1 + diff;
899         if (remainingBytes >= 2) {
900             return UNSAFE.getChar(bytes1, baseOffset1) == UNSAFE.getChar(bytes2, baseOffset2) &&
901                     (remainingBytes == 2 ||
902                     UNSAFE.getByte(bytes1, baseOffset1 + 2) == UNSAFE.getByte(bytes2, baseOffset2 + 2));
903         }
904         return remainingBytes == 0 ||
905                 UNSAFE.getByte(bytes1, baseOffset1) == UNSAFE.getByte(bytes2, baseOffset2);
906     }
907 
908     static int equalsConstantTime(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
909         long result = 0;
910         long remainingBytes = length & 7;
911         final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1;
912         final long end = baseOffset1 + remainingBytes;
913         final long diff = startPos2 - startPos1;
914         for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) {
915             result |= UNSAFE.getLong(bytes1, i) ^ UNSAFE.getLong(bytes2, i + diff);
916         }
917         if (remainingBytes >= 4) {
918             result |= UNSAFE.getInt(bytes1, baseOffset1) ^ UNSAFE.getInt(bytes2, baseOffset1 + diff);
919             remainingBytes -= 4;
920         }
921         if (remainingBytes >= 2) {
922             long pos = end - remainingBytes;
923             result |= UNSAFE.getChar(bytes1, pos) ^ UNSAFE.getChar(bytes2, pos + diff);
924             remainingBytes -= 2;
925         }
926         if (remainingBytes == 1) {
927             long pos = end - 1;
928             result |= UNSAFE.getByte(bytes1, pos) ^ UNSAFE.getByte(bytes2, pos + diff);
929         }
930         return ConstantTimeUtils.equalsConstantTime(result, 0);
931     }
932 
933     static boolean isZero(byte[] bytes, int startPos, int length) {
934         if (length <= 0) {
935             return true;
936         }
937         final long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
938         int remainingBytes = length & 7;
939         final long end = baseOffset + remainingBytes;
940         for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
941             if (UNSAFE.getLong(bytes, i) != 0) {
942                 return false;
943             }
944         }
945 
946         if (remainingBytes >= 4) {
947             remainingBytes -= 4;
948             if (UNSAFE.getInt(bytes, baseOffset + remainingBytes) != 0) {
949                 return false;
950             }
951         }
952         if (remainingBytes >= 2) {
953             return UNSAFE.getChar(bytes, baseOffset) == 0 &&
954                     (remainingBytes == 2 || bytes[startPos + 2] == 0);
955         }
956         return bytes[startPos] == 0;
957     }
958 
959     static int hashCodeAscii(byte[] bytes, int startPos, int length) {
960         int hash = HASH_CODE_ASCII_SEED;
961         long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
962         final int remainingBytes = length & 7;
963         final long end = baseOffset + remainingBytes;
964         for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
965             hash = hashCodeAsciiCompute(UNSAFE.getLong(bytes, i), hash);
966         }
967         if (remainingBytes == 0) {
968             return hash;
969         }
970         int hcConst = HASH_CODE_C1;
971         if (remainingBytes != 2 & remainingBytes != 4 & remainingBytes != 6) { // 1, 3, 5, 7
972             hash = hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset));
973             hcConst = HASH_CODE_C2;
974             baseOffset++;
975         }
976         if (remainingBytes != 1 & remainingBytes != 4 & remainingBytes != 5) { // 2, 3, 6, 7
977             hash = hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset));
978             hcConst = hcConst == HASH_CODE_C1 ? HASH_CODE_C2 : HASH_CODE_C1;
979             baseOffset += 2;
980         }
981         if (remainingBytes >= 4) { // 4, 5, 6, 7
982             return hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset));
983         }
984         return hash;
985     }
986 
987     static int hashCodeAsciiCompute(long value, int hash) {
988         // masking with 0x1f reduces the number of overall bits that impact the hash code but makes the hash
989         // code the same regardless of character case (upper case or lower case hash is the same).
990         return hash * HASH_CODE_C1 +
991                 // Low order int
992                 hashCodeAsciiSanitize((int) value) * HASH_CODE_C2 +
993                 // High order int
994                 (int) ((value & 0x1f1f1f1f00000000L) >>> 32);
995     }
996 
997     static int hashCodeAsciiSanitize(int value) {
998         return value & 0x1f1f1f1f;
999     }
1000 
1001     static int hashCodeAsciiSanitize(short value) {
1002         return value & 0x1f1f;
1003     }
1004 
1005     static int hashCodeAsciiSanitize(byte value) {
1006         return value & 0x1f;
1007     }
1008 
1009     static ClassLoader getClassLoader(final Class<?> clazz) {
1010         if (System.getSecurityManager() == null) {
1011             return clazz.getClassLoader();
1012         } else {
1013             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
1014                 @Override
1015                 public ClassLoader run() {
1016                     return clazz.getClassLoader();
1017                 }
1018             });
1019         }
1020     }
1021 
1022     static ClassLoader getContextClassLoader() {
1023         if (System.getSecurityManager() == null) {
1024             return Thread.currentThread().getContextClassLoader();
1025         } else {
1026             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
1027                 @Override
1028                 public ClassLoader run() {
1029                     return Thread.currentThread().getContextClassLoader();
1030                 }
1031             });
1032         }
1033     }
1034 
1035     static ClassLoader getSystemClassLoader() {
1036         if (System.getSecurityManager() == null) {
1037             return ClassLoader.getSystemClassLoader();
1038         } else {
1039             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
1040                 @Override
1041                 public ClassLoader run() {
1042                     return ClassLoader.getSystemClassLoader();
1043                 }
1044             });
1045         }
1046     }
1047 
1048     static int addressSize() {
1049         return UNSAFE.addressSize();
1050     }
1051 
1052     static long allocateMemory(long size) {
1053         return UNSAFE.allocateMemory(size);
1054     }
1055 
1056     static void freeMemory(long address) {
1057         UNSAFE.freeMemory(address);
1058     }
1059 
1060     static long reallocateMemory(long address, long newSize) {
1061         return UNSAFE.reallocateMemory(address, newSize);
1062     }
1063 
1064     static boolean isAndroid() {
1065         return IS_ANDROID;
1066     }
1067 
1068     private static boolean isAndroid0() {
1069         // Idea: Sometimes java binaries include Android classes on the classpath, even if it isn't actually Android.
1070         // Rather than check if certain classes are present, just check the VM, which is tied to the JDK.
1071 
1072         // Optional improvement: check if `android.os.Build.VERSION` is >= 24. On later versions of Android, the
1073         // OpenJDK is used, which means `Unsafe` will actually work as expected.
1074 
1075         // Android sets this property to Dalvik, regardless of whether it actually is.
1076         String vmName = SystemPropertyUtil.get("java.vm.name");
1077         boolean isAndroid = "Dalvik".equals(vmName);
1078         if (isAndroid) {
1079             logger.debug("Platform: Android");
1080         }
1081         return isAndroid;
1082     }
1083 
1084     private static boolean explicitTryReflectionSetAccessible0() {
1085         // we disable reflective access
1086         return SystemPropertyUtil.getBoolean("io.netty.tryReflectionSetAccessible",
1087                 javaVersion() < 9 || RUNNING_IN_NATIVE_IMAGE);
1088     }
1089 
1090     static boolean isExplicitTryReflectionSetAccessible() {
1091         return IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE;
1092     }
1093 
1094     static int javaVersion() {
1095         return JAVA_VERSION;
1096     }
1097 
1098     private static int javaVersion0() {
1099         final int majorVersion;
1100 
1101         if (isAndroid()) {
1102             majorVersion = 6;
1103         } else {
1104             majorVersion = majorVersionFromJavaSpecificationVersion();
1105         }
1106 
1107         logger.debug("Java version: {}", majorVersion);
1108 
1109         return majorVersion;
1110     }
1111 
1112     // Package-private for testing only
1113     static int majorVersionFromJavaSpecificationVersion() {
1114         return majorVersion(SystemPropertyUtil.get("java.specification.version", "1.6"));
1115     }
1116 
1117     // Package-private for testing only
1118     static int majorVersion(final String javaSpecVersion) {
1119         final String[] components = javaSpecVersion.split("\\.");
1120         final int[] version = new int[components.length];
1121         for (int i = 0; i < components.length; i++) {
1122             version[i] = Integer.parseInt(components[i]);
1123         }
1124 
1125         if (version[0] == 1) {
1126             assert version[1] >= 6;
1127             return version[1];
1128         } else {
1129             return version[0];
1130         }
1131     }
1132 
1133     private PlatformDependent0() {
1134     }
1135 }