Patch-Source: https://github.com/graalvm/mandrel/commit/cedccd02520a6d9888e9cb5b2f6b505fca79dd7a
--
From cedccd02520a6d9888e9cb5b2f6b505fca79dd7a Mon Sep 17 00:00:00 2001
From: Foivos Zakkak <fzakkak@redhat.com>
Date: Thu, 9 Nov 2023 11:10:10 +0200
Subject: [PATCH] Fix `javax.crypto.JceSecurity` substitutions in JDK >=
 17.0.10

Closes https://github.com/graalvm/mandrel/issues/607
---
 ...eSecurityHasInnerClassIdentityWrapper.java | 41 +++++++++++++++++++
 ...urityHasInnerClassWeakIdentityWrapper.java | 41 +++++++++++++++++++
 .../svm/core/jdk/SecuritySubstitutions.java   | 36 ++++++++++++++--
 .../svm/hosted/SecurityServicesFeature.java   | 32 ++++++++++++++-
 4 files changed, 145 insertions(+), 5 deletions(-)
 create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JceSecurityHasInnerClassIdentityWrapper.java
 create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JceSecurityHasInnerClassWeakIdentityWrapper.java

diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JceSecurityHasInnerClassIdentityWrapper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JceSecurityHasInnerClassIdentityWrapper.java
new file mode 100644
index 000000000000..393d47004794
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JceSecurityHasInnerClassIdentityWrapper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.svm.core.jdk;
+
+import java.util.function.BooleanSupplier;
+
+public class JceSecurityHasInnerClassIdentityWrapper implements BooleanSupplier {
+
+    @Override
+    public boolean getAsBoolean() {
+        try {
+            Class.forName("javax.crypto.JceSecurity$IdentityWrapper");
+            return true;
+        } catch (ClassNotFoundException e) {
+            return false;
+        }
+    }
+
+}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JceSecurityHasInnerClassWeakIdentityWrapper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JceSecurityHasInnerClassWeakIdentityWrapper.java
new file mode 100644
index 000000000000..40f5e8732eb7
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JceSecurityHasInnerClassWeakIdentityWrapper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.svm.core.jdk;
+
+import java.util.function.BooleanSupplier;
+
+public class JceSecurityHasInnerClassWeakIdentityWrapper implements BooleanSupplier {
+
+    @Override
+    public boolean getAsBoolean() {
+        try {
+            Class.forName("javax.crypto.JceSecurity$WeakIdentityWrapper");
+            return true;
+        } catch (ClassNotFoundException e) {
+            return false;
+        }
+    }
+
+}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java
index f4fead83b3e7..d769996b731f 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 
 import static com.oracle.svm.core.snippets.KnownIntrinsics.readCallerStackPointer;
 
+import java.lang.ref.ReferenceQueue;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.net.URL;
@@ -43,6 +44,7 @@
 import java.security.SecureRandom;
 import java.util.List;
 import java.util.Map;
+import java.util.function.BooleanSupplier;
 import java.util.function.Predicate;
 
 import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
@@ -317,10 +319,26 @@ static boolean isTrustedCryptoProvider(Provider provider) {
     }
 }
 
+final class QueueFieldPresent implements BooleanSupplier {
+    @Override
+    public boolean getAsBoolean() {
+        try {
+            Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
+            jceSecurity.getDeclaredField("queue");
+            return true;
+        } catch (ClassNotFoundException | NoSuchFieldException e) {
+            return false;
+        }
+    }
+}
+
 @TargetClass(className = "javax.crypto.JceSecurity")
 @SuppressWarnings({"unused"})
 final class Target_javax_crypto_JceSecurity {
 
+    @Alias @TargetElement(onlyWith = QueueFieldPresent.class)//
+    public static ReferenceQueue<Object> queue;
+
     /*
      * Lazily recompute the RANDOM field at runtime. We cannot push the entire static initialization
      * of JceSecurity to run time because we want the JceSecurity.verificationResults initialized at
@@ -393,8 +411,7 @@ public Object transform(Object receiver, Object originalValue) {
     }
 }
 
-@TargetClass(className = "javax.crypto.JceSecurity", innerClass = "IdentityWrapper", onlyWith = JDK17OrLater.class)
-@SuppressWarnings({"unused"})
+@TargetClass(className = "javax.crypto.JceSecurity", innerClass = "IdentityWrapper", onlyWith = JceSecurityHasInnerClassIdentityWrapper.class)
 final class Target_javax_crypto_JceSecurity_IdentityWrapper {
     @Alias //
     Provider obj;
@@ -405,6 +422,14 @@ final class Target_javax_crypto_JceSecurity_IdentityWrapper {
     }
 }
 
+@TargetClass(className = "javax.crypto.JceSecurity", innerClass = "WeakIdentityWrapper", onlyWith = JceSecurityHasInnerClassWeakIdentityWrapper.class)
+final class Target_javax_crypto_JceSecurity_WeakIdentityWrapper {
+    @Alias //
+    Target_javax_crypto_JceSecurity_WeakIdentityWrapper(Provider obj, ReferenceQueue<Object> queue) {
+        // Do nothing this is just an alias
+    }
+}
+
 class JceSecurityAccessor {
     private static volatile SecureRandom RANDOM;
 
@@ -436,7 +461,12 @@ static Object providerKey(Provider p) {
         if (JavaVersionUtil.JAVA_SPEC <= 11) {
             return p;
         }
+
         /* Starting with JDK 17 the verification results map key is an identity wrapper object. */
+        if (new JceSecurityHasInnerClassWeakIdentityWrapper().getAsBoolean()) {
+            return new Target_javax_crypto_JceSecurity_WeakIdentityWrapper(p, Target_javax_crypto_JceSecurity.queue);
+        }
+
         return new Target_javax_crypto_JceSecurity_IdentityWrapper(p);
     }
 
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java
index d5c4e9ea3dba..56e4790d7fbb 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,7 @@
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.ref.Reference;
 import java.lang.reflect.Executable;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
@@ -87,6 +88,7 @@
 import javax.xml.crypto.dsig.XMLSignatureFactory;
 import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
 
+import com.oracle.svm.core.jdk.JceSecurityHasInnerClassWeakIdentityWrapper;
 import org.graalvm.compiler.options.Option;
 import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
 import org.graalvm.nativeimage.ImageSingletons;
@@ -885,7 +887,33 @@ private Function<Object, Object> constructVerificationCacheCleaner(Class<?> jceS
             };
         }
         /*
-         * For JDK 17 and later, the verification cache is an IdentityWrapper -> Verification result
+         * For JDK 17.0.10 and later, the verification cache is a WeakIdentityWrapper ->
+         * Verification result ConcurrentHashMap. The WeakIdentityWrapper contains the actual
+         * provider in the 'obj' field.
+         */
+        if (new JceSecurityHasInnerClassWeakIdentityWrapper().getAsBoolean()) {
+            Method getReferent = ReflectionUtil.lookupMethod(Reference.class, "get");
+            Predicate<Object> listRemovalPredicate = wrapper -> {
+                try {
+                    return shouldRemoveProvider((Provider) getReferent.invoke(wrapper));
+                } catch (IllegalAccessException | InvocationTargetException e) {
+                    throw VMError.shouldNotReachHere(e);
+                }
+            };
+
+            return obj -> {
+                Map<Object, Object> original = (Map<Object, Object>) obj;
+                Map<Object, Object> verificationResults = new ConcurrentHashMap<>(original);
+
+                verificationResults.keySet().removeIf(listRemovalPredicate);
+
+                return verificationResults;
+            };
+
+        }
+
+        /*
+         * For JDK 17 up to 17.0.10, the verification cache is an IdentityWrapper -> Verification result
          * ConcurrentHashMap. The IdentityWrapper contains the actual provider in the 'obj' field.
          */
         Class<?> identityWrapper = loader.findClassOrFail("javax.crypto.JceSecurity$IdentityWrapper");
