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