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