From 5a584254549ece0b17f3ef8388556d38e24f270e Mon Sep 17 00:00:00 2001 From: Sam Berlin Date: Thu, 4 May 2023 12:45:53 -0700 Subject: [PATCH] Rework sun.misc.Unsafe usages so that we directly refer to sun.misc.Unsafe, and call getObject on it natively instead of through reflection. (Take 2, after exempting its usages internally.) Calling getObject through reflection breaks Azul JVMs, because the staticFieldBase is a funny "not object", but when passed through reflection it tries to become an Object and fails. Retrieval of the unsafe is based on how other google open source projects do it, see for example AbstractFuture, UnsignedBytes, and protobufs. Fixes #1719, and properly fixes #1672. (Note that this change also bumps the Github Action's bazel version from 4.2.2 to 6.1.2, because somewhere along the way from 4.2.2->6.1.2, Bazel fixed the --javacopts="--release 8" option to also allow sun.misc.Unsafe.) PiperOrigin-RevId: 529486761 --- .github/workflows/ci.yml | 2 +- .../internal/aop/AnonymousClassDefiner.java | 12 +++--- .../internal/aop/HiddenClassDefiner.java | 18 ++++----- .../inject/internal/aop/UnsafeGetter.java | 40 +++++++++++++++++++ 4 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 core/src/com/google/inject/internal/aop/UnsafeGetter.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ba4f4faea..d9cd933db8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven name: continuous-integration env: - USE_BAZEL_VERSION: '4.2.2' + USE_BAZEL_VERSION: '6.1.2' USE_JAVA_DISTRIBUTION: 'zulu' USE_JAVA_VERSION: '11' diff --git a/core/src/com/google/inject/internal/aop/AnonymousClassDefiner.java b/core/src/com/google/inject/internal/aop/AnonymousClassDefiner.java index 87c47a6ba6..e26d20ea41 100644 --- a/core/src/com/google/inject/internal/aop/AnonymousClassDefiner.java +++ b/core/src/com/google/inject/internal/aop/AnonymousClassDefiner.java @@ -16,7 +16,6 @@ package com.google.inject.internal.aop; -import java.lang.reflect.Field; import java.lang.reflect.Method; /** @@ -26,17 +25,16 @@ */ final class AnonymousClassDefiner implements ClassDefiner { - private static final Object THE_UNSAFE; + private static final sun.misc.Unsafe THE_UNSAFE; private static final Method ANONYMOUS_DEFINE_METHOD; static { try { - Class unsafeType = Class.forName("sun.misc.Unsafe"); - Field theUnsafeField = unsafeType.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - THE_UNSAFE = theUnsafeField.get(null); + THE_UNSAFE = UnsafeGetter.getUnsafe(); + // defineAnonymousClass was removed in JDK17, so we must refer to it reflectively. ANONYMOUS_DEFINE_METHOD = - unsafeType.getMethod("defineAnonymousClass", Class.class, byte[].class, Object[].class); + sun.misc.Unsafe.class.getMethod( + "defineAnonymousClass", Class.class, byte[].class, Object[].class); } catch (ReflectiveOperationException e) { throw new ExceptionInInitializerError(e); } diff --git a/core/src/com/google/inject/internal/aop/HiddenClassDefiner.java b/core/src/com/google/inject/internal/aop/HiddenClassDefiner.java index af36f7be0c..1486cb8c93 100644 --- a/core/src/com/google/inject/internal/aop/HiddenClassDefiner.java +++ b/core/src/com/google/inject/internal/aop/HiddenClassDefiner.java @@ -28,22 +28,18 @@ */ final class HiddenClassDefiner implements ClassDefiner { - private static final Object THE_UNSAFE; - private static final Object TRUSTED_LOOKUP_OFFSET; - private static final Method GET_OBJECT_METHOD; + private static final sun.misc.Unsafe THE_UNSAFE; + private static final Object TRUSTED_LOOKUP_BASE; + private static final long TRUSTED_LOOKUP_OFFSET; private static final Object HIDDEN_CLASS_OPTIONS; private static final Method HIDDEN_DEFINE_METHOD; static { try { - Class unsafeType = Class.forName("sun.misc.Unsafe"); - Field theUnsafeField = unsafeType.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - THE_UNSAFE = theUnsafeField.get(null); + THE_UNSAFE = UnsafeGetter.getUnsafe(); Field trustedLookupField = Lookup.class.getDeclaredField("IMPL_LOOKUP"); - Method offsetMethod = unsafeType.getMethod("staticFieldOffset", Field.class); - TRUSTED_LOOKUP_OFFSET = offsetMethod.invoke(THE_UNSAFE, trustedLookupField); - GET_OBJECT_METHOD = unsafeType.getMethod("getObject", Object.class, long.class); + TRUSTED_LOOKUP_BASE = THE_UNSAFE.staticFieldBase(trustedLookupField); + TRUSTED_LOOKUP_OFFSET = THE_UNSAFE.staticFieldOffset(trustedLookupField); HIDDEN_CLASS_OPTIONS = classOptions("NESTMATE"); HIDDEN_DEFINE_METHOD = Lookup.class.getMethod( @@ -56,7 +52,7 @@ final class HiddenClassDefiner implements ClassDefiner { @Override public Class define(Class hostClass, byte[] bytecode) throws Exception { Lookup trustedLookup = - (Lookup) GET_OBJECT_METHOD.invoke(THE_UNSAFE, Lookup.class, TRUSTED_LOOKUP_OFFSET); + (Lookup) THE_UNSAFE.getObject(TRUSTED_LOOKUP_BASE, TRUSTED_LOOKUP_OFFSET); Lookup definedLookup = (Lookup) HIDDEN_DEFINE_METHOD.invoke( diff --git a/core/src/com/google/inject/internal/aop/UnsafeGetter.java b/core/src/com/google/inject/internal/aop/UnsafeGetter.java new file mode 100644 index 0000000000..c7221b658f --- /dev/null +++ b/core/src/com/google/inject/internal/aop/UnsafeGetter.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.inject.internal.aop; + +final class UnsafeGetter { + + private UnsafeGetter() {} + + static sun.misc.Unsafe getUnsafe() throws ReflectiveOperationException { + try { + return sun.misc.Unsafe.getUnsafe(); + } catch (SecurityException unusedFallbackToReflection) { + } + // Note that we do not do this in a privileged action because we expect we're already in a + // privileged block (from UnsafeClassDefiner). + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k.getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if (k.isInstance(x)) { + return k.cast(x); + } + } + throw new NoSuchFieldError("the Unsafe"); + } +}