premain: Crash in Infinispan server code caused by NPE under MethodHandleNative::linkDynamicConstant

ioi.lam at oracle.com ioi.lam at oracle.com
Fri Sep 20 00:51:05 UTC 2024


Upon further investigation, it seems a simple patch might fix the 
problem with classData.

I tested with 
test/hotspot/jtreg/runtime/cds/appcds/LambdaWithUseImplMethodHandle.java 
by running jtreg with -javaoptions:-XX:+AOTClassLinking
Without the patch, I'd get a NullPointerException. With the patch, the 
test passes.

It looks like classData is not treated as a static field of the class, 
but rather an instance field in the mirror. So classData wasn't copied 
by the loop in HeapShared::copy_preinitialized_mirror().

Ashutosh, could you try it this fixes the crash on your side?

Thanks

- Ioi

diff --git a/src/hotspot/share/cds/heapShared.cpp 
b/src/hotspot/share/cds/heapShared.cpp
index 0e70778f057..62cf142f67a 100644
--- a/src/hotspot/share/cds/heapShared.cpp
+++ b/src/hotspot/share/cds/heapShared.cpp
@@ -634,6 +634,8 @@ void HeapShared::copy_preinitialized_mirror(Klass* 
orig_k, oop orig_mirror, oop
      }
    }

+  java_lang_Class::set_class_data(m, 
java_lang_Class::class_data(orig_mirror));
+
    // Class::reflectData use SoftReference, which cannot be archived. 
Set it
    // to null and it will be recreated at runtime.
    java_lang_Class::set_reflection_data(m, nullptr);



On 9/19/24 12:51 PM, ioi.lam at oracle.com wrote:
>
>
> On 9/19/24 7:44 AM, Ashutosh Mehra wrote:
>>
>>     As I am cleaning up the code for upstreaming to mainline, I am
>>     going add an equivalent check in the C code to filter out these
>>     indy call sites, so they won't be resolved at all during the
>>     assembly phase. Otherwise, I will run into problems described in
>>     https://bugs.openjdk.org/browse/JDK-8290417
>>
>>
>> Thanks for the link to the bug. The scenario described in that bug is 
>> exactly the same as the Infinispan case.
>> So if we filter out such cases during indy resolution then it should 
>> resolve the Infinispan issue as well.
>>
>> A basic question: why can't CDS handle the lambda proxy class 
>> generated in useImplMethodHandle mode?
>>
> If I remember correctly, it has to do with the shape of dynamically 
> generated bytecode:
>
>   public void accept(java.lang.Object);
>     Code:
>        0: ldc #28 // Dynamic #0:_:Ljava/lang/invoke/MethodHandle;
>        2: aload_0
>        3: getfield #15 // Field arg$1:LTester;
>        6: aload_1
>        7: checkcast #30 // class java/lang/String
>       10: invokevirtual #36 // Method 
> java/lang/invoke/MethodHandle.invokeExact:(LTester;Ljava/lang/String;)V
>       13: return
>
> The result of the "ldc" was not symbolically encoded in the generated 
> class (as the generated class has no permission to access that 
> method). So the MethodHandle is stored as a binary object in the 
> mirror of this generated class (with the java.lang.Class::classData 
> field).
>
> Plain CDS doesn't archive class mirrors, so the classData will be lost.
>
> With JEP 483, we should be able to preserve the classData, so I am not 
> sure why the useImplMethodHandle case is still failing. My plan is to 
> filter these out for now, but I will get back to it later when I have 
> more time.
>
> Thanks
>
> - Ioi
>
>
>
>
>> Thanks,
>> - Ashutosh Mehra
>>
>>
>> On Thu, Sep 19, 2024 at 1:45 AM <ioi.lam at oracle.com> wrote:
>>
>>     Hi Ashutosh,
>>
>>     I have some update:
>>
>>     The original crash was caused by the "useImplMethodHandle" code
>>     in InnerClassLambdaMetafactory.java:
>>
>>             // If the target class invokes a protected method
>>     inherited from a
>>             // superclass in a different package, or does
>>     'invokespecial', the
>>             // lambda class has no access to the resolved method, or does
>>             // 'invokestatic' on a hidden class which cannot be
>>     resolved by name.
>>             // Instead, we need to pass the live implementation
>>     method handle to
>>             // the proxy class to invoke directly. (javac prefers to
>>     avoid this
>>             // situation by generating bridges in the target class)
>>             useImplMethodHandle =
>>     (Modifier.isProtected(implInfo.getModifiers()) &&
>>     !VerifyAccess.isSamePackage(targetClass,
>>     implInfo.getDeclaringClass())) ||
>>                                    implKind ==
>>     MethodHandleInfo.REF_invokeSpecial ||
>>                                    implKind ==
>>     MethodHandleInfo.REF_invokeStatic && implClass.isHidden();
>>
>>     As I am cleaning up the code for upstreaming to mainline, I am
>>     going add an equivalent check in the C code to filter out these
>>     indy call sites, so they won't be resolved at all during the
>>     assembly phase. Otherwise, I will run into problems described in
>>     https://bugs.openjdk.org/browse/JDK-8290417
>>
>>     Once I get the filtering code working, I will integrate it back
>>     to premain.
>>
>>     >I am wondering if we can workaround class circularity issues by
>>     recording class initialization order
>>     >during training run and use that to guide the initialization
>>     during assembly phase.
>>
>>     In the production run we take different paths than the training run
>>
>>     (1) some classes are aot-initialized (especially the enums)
>>     (2) some classes make special CDS calls
>>
>>     so I am not sure if it's possible to get the same initialization
>>     order as in the training run (or assembly phase).
>>
>>     (more below)
>>
>>     On 9/18/24 9:10 AM, Ashutosh Mehra wrote:
>>>     Hi Ioi,
>>>
>>>         I was having a similar circularity issue (but in production
>>>         time) and I just added enough classes to make the NPE go away.
>>>
>>>     I am wondering if you have a test case that reproduces the NPE
>>>     which prompted you to add these classes:
>>>
>>>     https://github.com/openjdk/leyden/blob/7781109154bf2af89854c7e13aa3e160bb82608e/src/hotspot/share/cds/aotClassInitializer.cpp#L65-L78
>>>     <https://urldefense.com/v3/__https://github.com/openjdk/leyden/blob/7781109154bf2af89854c7e13aa3e160bb82608e/src/hotspot/share/cds/aotClassInitializer.cpp*L65-L78__;Iw!!ACWV5N9M2RV99hQ!NYvqLbb_Ib2kSdm2fcYVLGby-AiB2TG7KtC1HQHVKLiM-_kRb5mOt8krV5IZfReaYZ0MPadDfa08hQ$>
>>>
>>>     I commented this code and ran the tests under premain but didn't
>>>     hit the NPE.
>>>
>>     I forgot what the problem was, but it happened for very simple cases.
>>
>>     Thanks
>>
>>     - Ioi
>>
>>
>>>     Thanks,
>>>     - Ashutosh Mehra
>>>
>>>
>>>     On Tue, Sep 17, 2024 at 1:11 AM <ioi.lam at oracle.com> wrote:
>>>
>>>         Hi Ashutosh,
>>>
>>>         So this looks like a potential bug (or feature) in the core
>>>         lib code. When CDS forcefully initializes a class in an
>>>         unexpected (or untested) order, the "initialization soup" fails.
>>>
>>>         Perhaps a work-around would be to make some harmless calls
>>>         at the place where CDS was calling into
>>>         MethodType.createArchivedObjects(). E.g., do something like
>>>         this:
>>>
>>>         +    if (CDSConfig::is_dumping_invokedynamic()) {
>>>         +       // call into Java:
>>>         jdk.internal.misc::warmupInvokeDynamic();
>>>         +   }
>>>             // Rewrite and link classes
>>>             log_info(cds)("Rewriting and linking classes ...");
>>>
>>>         Maybe you can add a new Lambda expressions, MethodHandle
>>>         invocations, etc, that would hopefully cause
>>>         PrimitiveClassDescImpl and friends to be initialized in
>>>         their natural order.
>>>
>>>         Or call class.forName("java.lang.constant.ConstantDescs") ??
>>>
>>>         BTW, you can see my comments in
>>>         AOTClassInitializer::is_forced_preinit_class():
>>>
>>>             // TODO:
>>>             // This is needed since JDK-8338532. Without this, when
>>>             // archived heap objects are used, the class init order
>>>         is not
>>>             // expected by the jdk/internal/constant bootstrap code
>>>         and we
>>>             // will get a null pointer exception.
>>>             //
>>>             // When bootstraping has intricated/fragile order, it's
>>>         probably
>>>             // better to archive all related classes in an
>>>         initialized state
>>>             // (i.e., take a snapshot). The existing approach in
>>>             // heapShared::resolve_or_init_classes_for_subgraph_of()
>>>         won't work.
>>>         "jdk/internal/constant/PrimitiveClassDescImpl",
>>>         "jdk/internal/constant/ReferenceClassDescImpl",
>>>             "java/lang/constant/ConstantDescs",
>>>             "sun/invoke/util/Wrapper",
>>>
>>>         I was having a similar circularity issue (but in production
>>>         time) and I just added enough classes to make the NPE go
>>>         away. For your test case, if you manage to fix in in the
>>>         assembly run but run into NPE in production run, you might
>>>         need to add more classes to this list. Yes, it's a hack :-(
>>>
>>>
>>>         Thanks
>>>
>>>         - Ioi
>>>
>>>         On 9/13/24 7:05 PM, Ashutosh Mehra wrote:
>>>>         This is turning out to be a real example of class
>>>>         initialization soup!
>>>>         As mentioned during the meeting, I am getting NPE in the
>>>>         assembly phase when testing the patch [0] that I proposed
>>>>         in my earlier mail
>>>>         using a test case inspired by the Infinispan code.
>>>>         NPE occurs when running the class initializer for
>>>>         PrimitiveClassDescImpl
>>>>         Interestingly, PrimitiveClassDescImpl is "forcefully"
>>>>         initialized by MetaspaceShared::link_shared_classes().
>>>>
>>>>         I couldn't get a stack trace so I relied on exception logs
>>>>         (using -Xlog:exceptions=trace) to find the cause which
>>>>         indicate following frames on the stack:
>>>>
>>>>         [0]
>>>>         jdk/internal/constant/MethodTypeDescImpl::validateArgument(Ljava/lang/constant/ClassDesc;)Ljava/lang/constant/ClassDesc; @
>>>>         bci 1
>>>>         [1]
>>>>         jdk/internal/constant/MethodTypeDescImpl::ofTrusted(Ljava/lang/constant/ClassDesc;[Ljava/lang/constant/ClassDesc;)Ljdk/internal/constant/MethodTypeDescImpl; @
>>>>         bci 27
>>>>         [2]
>>>>         java/lang/constant/ConstantDescs::ofConstantBootstrap(Ljava/lang/constant/ClassDesc;Ljava/lang/String;Ljava/lang/constant/ClassDesc;[Ljava/lang/constant/ClassDesc;)Ljava/lang/constant/DirectMethodHandleDesc; @
>>>>         bci 47
>>>>         [3] java/lang/constant/ConstantDescs::<clinit> @ bci 664
>>>>         [4]
>>>>         jdk/internal/constant/PrimitiveClassDescImpl::<init>(Ljava/lang/String;)V @
>>>>         bci 1
>>>>         [5]
>>>>         jdk/internal/constant/PrimitiveClassDescImpl::<clinit>(Ljava/lang/String;)V @
>>>>         bci 6
>>>>
>>>>         Notice that invocation of PrimitiveClassDescImpl::<clinit>
>>>>         results in initialization of ConstantDescs class (see frame 3).
>>>>         ConstantDescs::<clinit> @ 664 corresponds to following java
>>>>         code:
>>>>
>>>>             public static final DirectMethodHandleDesc
>>>>         BSM_CLASS_DATA_AT
>>>>           = ofConstantBootstrap(CD_MethodHandles, "classDataAt",
>>>>                     CD_Object, CD_int);
>>>>
>>>>         The last parameter CD_int is initialized as:
>>>>
>>>>             public static final ClassDesc CD_int =
>>>>         PrimitiveClassDescImpl.CD_int;
>>>>
>>>>         So, its value is obtained from
>>>>         PrimitiveClassDescImpl.CD_int which hasn't been initialized
>>>>         properly yet. As a result ConstantDescs::CD_int is
>>>>         assigned null which results in
>>>>         MethodTypeDescImpl::validateArgument throwing NPE later.
>>>>         There is a clear class initialization circularity involving
>>>>         PrimitiveClassDescImpl and ConstantDescs, and the result
>>>>         depends on which class gets initialized first.
>>>>
>>>>         Without my patch this issue is not seen because
>>>>         PrimitiveClassDescImpl has already been initialized by the
>>>>         time MetaspaceShared::link_shared_classes() is called.
>>>>         Its initialization is triggered by the call to
>>>>         MethodType::createArchivedObjects().
>>>>         It also explains why my patch introduced this issue because
>>>>         it effectively moved the call to
>>>>         MethodType::createArchivedObjects() after
>>>>         MetaspaceShared::link_shared_classes().
>>>>
>>>>         [0]
>>>>         https://github.com/ashu-mehra/leyden/commit/d8f99cce67df1c7b0f7ef8562676df438633a66e
>>>>         <https://urldefense.com/v3/__https://github.com/ashu-mehra/leyden/commit/d8f99cce67df1c7b0f7ef8562676df438633a66e__;!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhZK3GwoiA$>
>>>>
>>>>         Thanks,
>>>>         - Ashutosh Mehra
>>>>
>>>>
>>>>         On Wed, Sep 11, 2024 at 3:12 PM Ashutosh Mehra
>>>>         <asmehra at redhat.com> wrote:
>>>>
>>>>             Regarding the WrongMethodTypeException that I mentioned
>>>>             in my previous email (see pt 3),
>>>>             this exception happens when lambda proxy class attempts
>>>>             to invoke the MethodHandle it obtained from the classData:
>>>>
>>>>               public void accept(java.lang.Object);
>>>>                 descriptor: (Ljava/lang/Object;)V
>>>>                 flags: (0x0001) ACC_PUBLIC
>>>>                 Code:
>>>>                   stack=3, locals=2, args_size=2
>>>>                      0: ldc           #26         // Dynamic
>>>>             #0:_:Ljava/lang/invoke/MethodHandle;
>>>>                      2: aload_0
>>>>                      3: getfield      #13         // Field
>>>>             arg$1:Lorg/wildfly/security/WildFlyElytronBaseProvider;
>>>>                      6: aload_1
>>>>                      7: checkcast     #28         // class
>>>>             java/security/Provider$Service
>>>>                     10: invokevirtual #34         // Method
>>>>             java/lang/invoke/MethodHandle.invokeExact:(Lorg/wildfly/security/WildFlyElytronBaseProvider;Ljava/security/Provider$Service;)V
>>>>                     13: return
>>>>
>>>>             The scenario is during the assembly phase as part of
>>>>             the indy resolution the MethodHandle for which the
>>>>             exception is thrown gets created.
>>>>             Normally MethodHandle's type gets added in
>>>>             MethodType::internTable but by the time indy resolution
>>>>             happens, JVM has already taken
>>>>             snapshot of the MethodType::internTable through an
>>>>             upcall to MethodType::createArchivedObjects().
>>>>             As a result the AOTCache ends up with the MethodType
>>>>             object which is not in AOTHolder.archivedMethodTypes.
>>>>
>>>>             During the production run, when the jvm invokes the
>>>>             MethodHandle, it searches for the MethodType
>>>>             corresponding to the signature passed at the callsite.
>>>>             As expected, it fails to find it in the
>>>>             AOTHolder.archivedMethodTypes, so it creates a new
>>>>             instance of the MethodType.
>>>>             But Invokers.checkExactType() relies on the
>>>>             MethodHandle's type to be the same object as the
>>>>             MethodType object passed as parameter.
>>>>
>>>>                 static void checkExactType(MethodHandle mhM,
>>>>             MethodType expected) {
>>>>             MethodType targetType = mh.type();
>>>>                     if (targetType != expected)
>>>>                         throw
>>>>             newWrongMethodTypeException(targetType, expected);
>>>>                 }
>>>>
>>>>             Hence, it throws WrongMethodTypeException though the
>>>>             two MT objects have the same signature.
>>>>
>>>>             To handle this scenario, I changed the order of indy
>>>>             resolution and upcall to
>>>>             MethodType::createArchivedObjects() as:
>>>>
>>>>             diff --git a/src/hotspot/share/cds/metaspaceShared.cpp
>>>>             b/src/hotspot/share/cds/metaspaceShared.cpp
>>>>             index df4bcadefa3..457716cac5b 100644
>>>>             --- a/src/hotspot/share/cds/metaspaceShared.cpp
>>>>             +++ b/src/hotspot/share/cds/metaspaceShared.cpp
>>>>             @@ -751,6 +751,20 @@ void
>>>>             MetaspaceShared::link_shared_classes(bool jcmd_request,
>>>>             TRAPS) {
>>>>                if (CDSConfig::is_dumping_final_static_archive()) {
>>>>              FinalImageRecipes::apply_recipes(CHECK);
>>>>                }
>>>>             +
>>>>             +#if INCLUDE_CDS_JAVA_HEAP
>>>>             +  if (CDSConfig::is_dumping_invokedynamic()) {
>>>>             +    // This makes sure that the MethodType and
>>>>             MethodTypeForm tables won't be updated
>>>>             +    // concurrently when we are saving their contents
>>>>             into a side table.
>>>>             +  assert(CDSConfig::allow_only_single_java_thread(),
>>>>             "Required");
>>>>             +
>>>>             +    JavaValue result(T_VOID);
>>>>             +  JavaCalls::call_static(&result,
>>>>             vmClasses::MethodType_klass(),
>>>>             + vmSymbols::createArchivedObjects(),
>>>>             + vmSymbols::void_method_signature(),
>>>>             +                           CHECK);
>>>>             +  }
>>>>             +#endif
>>>>              }
>>>>             Note that indy resolution happens as part of
>>>>             FinalImageRecipes::apply_recipes(CHECK) which is now
>>>>             invoked before the upcall to createArchivedObjects().
>>>>             With this change I am able to run the application
>>>>             without any exceptions.
>>>>             My complete patch can be seen here:
>>>>             https://github.com/ashu-mehra/leyden/commit/d8f99cce67df1c7b0f7ef8562676df438633a66e
>>>>             <https://urldefense.com/v3/__https://github.com/ashu-mehra/leyden/commit/d8f99cce67df1c7b0f7ef8562676df438633a66e__;!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhZK3GwoiA$>
>>>>             I will do more testing with this patch.
>>>>
>>>>             @Ioi Lam <mailto:ioi.lam at oracle.com> do you have any
>>>>             feedback on this patch.
>>>>
>>>>             Thanks,
>>>>             - Ashutosh Mehra
>>>>
>>>>
>>>>
>>>>             On Wed, Sep 11, 2024 at 10:14 AM Ashutosh Mehra
>>>>             <asmehra at redhat.com> wrote:
>>>>
>>>>                 Hi Andrew,
>>>>                 Thanks for sharing the initial investigation.
>>>>                 I have been looking into this and have a few of
>>>>                 things to add to your analysis:
>>>>
>>>>                 1.  As you mentioned the classData for the lambda
>>>>                 class WildFlyElytronBaseProvider$$Lambda is null.
>>>>                 The classData is stored in the mirror object of the
>>>>                 InstanceKlass when the class is defined
>>>>                 through JVM_LookupDefineClass.
>>>>                 However, when we create the scratch mirror object
>>>>                 (which get stored in the AOT cache) the classData
>>>>                 is not populated.
>>>>                 See
>>>>                 https://github.com/openjdk/leyden/blob/d23b9f2d5e3523cc547337da59327ed86a6057a3/src/hotspot/share/classfile/javaClasses.cpp#L1128-L1131
>>>>                 <https://urldefense.com/v3/__https://github.com/openjdk/leyden/blob/d23b9f2d5e3523cc547337da59327ed86a6057a3/src/hotspot/share/classfile/javaClasses.cpp*L1128-L1131__;Iw!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhbKjzG3gw$>
>>>>
>>>>                   Handle classData; // set to null. Will be
>>>>                 reinitialized at runtime
>>>>                   Handle mirror;
>>>>                   Handle comp_mirror;
>>>>                   allocate_mirror(k, /*is_scratch=*/true,
>>>>                 protection_domain, classData, mirror, comp_mirror,
>>>>                 CHECK);
>>>>
>>>>                 So this explains why the call to
>>>>                 classData(caller.lookupClass())returned null.
>>>>
>>>>                 2. In the mainline there is a check
>>>>                 in InnerClassLambdaMetafactory.java for the
>>>>                 particular code pattern used by the application.
>>>>                 If this code pattern is found then the lambda proxy
>>>>                 class is not included in the CDS archive.
>>>>                 See
>>>>                 https://github.com/openjdk/leyden/blob/d23b9f2d5e3523cc547337da59327ed86a6057a3/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java#L163-L170
>>>>                 <https://urldefense.com/v3/__https://github.com/openjdk/leyden/blob/d23b9f2d5e3523cc547337da59327ed86a6057a3/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java*L163-L170__;Iw!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhZl2S9tQg$>
>>>>                 and
>>>>                 https://github.com/openjdk/leyden/blob/d23b9f2d5e3523cc547337da59327ed86a6057a3/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java#L246
>>>>                 <https://urldefense.com/v3/__https://github.com/openjdk/leyden/blob/d23b9f2d5e3523cc547337da59327ed86a6057a3/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java*L246__;Iw!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhYsI3Xx0g$>
>>>>
>>>>                 // If the target class invokes a protected method
>>>>                 inherited from a
>>>>                         // superclass in a different package, or
>>>>                 does 'invokespecial', the
>>>>                         // lambda class has no access to the
>>>>                 resolved method, or does
>>>>                         // 'invokestatic' on a hidden class which
>>>>                 cannot be resolved by name.
>>>>                         // Instead, we need to pass the live
>>>>                 implementation method handle to
>>>>                         // the proxy class to invoke directly.
>>>>                 (javac prefers to avoid this
>>>>                         // situation by generating bridges in the
>>>>                 target class)
>>>>                         useImplMethodHandle =
>>>>                 (Modifier.isProtected(implInfo.getModifiers()) &&
>>>>                  !VerifyAccess.isSamePackage(targetClass,
>>>>                 implInfo.getDeclaringClass())) ||
>>>>                  implKind == MethodHandleInfo.REF_invokeSpecial ||
>>>>                  implKind == MethodHandleInfo.REF_invokeStatic &&
>>>>                 implClass.isHidden();
>>>>
>>>>                 In premain lambda proxy classes get included in the
>>>>                 AOT cache as a result of indy resolution and that
>>>>                 mechanism doesn't have this kind of check.
>>>>
>>>>                 3. For the null classData problem mentioned above,
>>>>                 I tried to fix it by storing classData in the
>>>>                 scratch mirror using the following patch:
>>>>
>>>>                 diff --git
>>>>                 a/src/hotspot/share/classfile/javaClasses.cpp
>>>>                 b/src/hotspot/share/classfile/javaClasses.cpp
>>>>                 index bd8141adbcc..41766e98093 100644
>>>>                 --- a/src/hotspot/share/classfile/javaClasses.cpp
>>>>                 +++ b/src/hotspot/share/classfile/javaClasses.cpp
>>>>                 @@ -1094,9 +1094,9 @@ void
>>>>                 java_lang_Class::create_mirror(Klass* k, Handle
>>>>                 class_loader,
>>>>                      }
>>>>                      if (CDSConfig::is_dumping_heap()) {
>>>>                        if
>>>>                 (CDSConfig::is_dumping_protection_domains()) {
>>>>                 -  create_scratch_mirror(k, protection_domain, CHECK);
>>>>                 +  create_scratch_mirror(k, protection_domain,
>>>>                 classData, CHECK);
>>>>                        } else {
>>>>                 -  create_scratch_mirror(k, Handle() /* null
>>>>                 protection_domain*/, CHECK);
>>>>                 +  create_scratch_mirror(k, Handle() /* null
>>>>                 protection_domain*/, classData, CHECK);
>>>>                        }
>>>>                      }
>>>>                    } else {
>>>>                 @@ -1117,7 +1117,7 @@ void
>>>>                 java_lang_Class::create_mirror(Klass* k, Handle
>>>>                 class_loader,
>>>>                  // Note: we archive the "scratch mirror" instead
>>>>                 of k->java_mirror(), because the
>>>>                  // latter may contain dumptime-specific
>>>>                 information that cannot be archived
>>>>                  // (e.g., ClassLoaderData*, or static fields that
>>>>                 are modified by Java code execution).
>>>>                 -void java_lang_Class::create_scratch_mirror(Klass*
>>>>                 k, Handle protection_domain, TRAPS) {
>>>>                 +void java_lang_Class::create_scratch_mirror(Klass*
>>>>                 k, Handle protection_domain, Handle classData, TRAPS) {
>>>>                    if (k->class_loader() != nullptr &&
>>>>                        k->class_loader() !=
>>>>                 SystemDictionary::java_platform_loader() &&
>>>>                        k->class_loader() !=
>>>>                 SystemDictionary::java_system_loader()) {
>>>>                 @@ -1125,9 +1125,11 @@ void
>>>>                 java_lang_Class::create_scratch_mirror(Klass* k,
>>>>                 Handle protection_domain,
>>>>                      return;
>>>>                    }
>>>>
>>>>                 -  Handle classData; // set to null. Will be
>>>>                 reinitialized at runtime
>>>>                 +  //Handle classData; // set to null. Will be
>>>>                 reinitialized at runtime
>>>>                    Handle mirror;
>>>>                    Handle comp_mirror;
>>>>                    allocate_mirror(k, /*is_scratch=*/true,
>>>>                 protection_domain, classData, mirror, comp_mirror,
>>>>                 CHECK);
>>>>
>>>>                    if (comp_mirror() != nullptr) {
>>>>                 diff --git
>>>>                 a/src/hotspot/share/classfile/javaClasses.hpp
>>>>                 b/src/hotspot/share/classfile/javaClasses.hpp
>>>>                 index bc49a0861a7..7ec2a2556dd 100644
>>>>                 --- a/src/hotspot/share/classfile/javaClasses.hpp
>>>>                 +++ b/src/hotspot/share/classfile/javaClasses.hpp
>>>>                 @@ -263,7 +263,7 @@ class java_lang_Class : AllStatic {
>>>>
>>>>                    // Archiving
>>>>                    static void serialize_offsets(SerializeClosure*
>>>>                 f) NOT_CDS_RETURN;
>>>>                 -  static void create_scratch_mirror(Klass* k,
>>>>                 Handle protection_domain, TRAPS)
>>>>                 NOT_CDS_JAVA_HEAP_RETURN;
>>>>                 +  static void create_scratch_mirror(Klass* k,
>>>>                 Handle protection_domain, Handle classData, TRAPS)
>>>>                 NOT_CDS_JAVA_HEAP_RETURN;
>>>>                    static bool restore_archived_mirror(Klass *k,
>>>>                 Handle class_loader, Handle module,
>>>>                        Handle protection_domain,
>>>>                        TRAPS) NOT_CDS_JAVA_HEAP_RETURN_(false);
>>>>
>>>>                 But this resulted in a different exception:
>>>>
>>>>                 Exception in thread "main"
>>>>                 java.lang.ExceptionInInitializerError
>>>>                 at com.redhat.leyden.Main.main(Main.java:7)
>>>>                 Caused by:
>>>>                 java.lang.invoke.WrongMethodTypeException: handle's
>>>>                 method type
>>>>                 (WildFlyElytronBaseProvider,Service)void but found
>>>>                 (WildFlyElytronBaseProvider,Service)void
>>>>                 at
>>>>                 java.base/java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:521)
>>>>                 at
>>>>                 java.base/java.lang.invoke.Invokers.checkExactType(Invokers.java:530)
>>>>                 at
>>>>                 java.base/java.lang.invoke.Invokers$Holder.invokeExact_MT(Invokers$Holder)
>>>>                 at
>>>>                 org.wildfly.security.WildFlyElytronBaseProvider$$Lambda/0x80000000c.accept(Unknown
>>>>                 Source)
>>>>                 at
>>>>                 org.wildfly.security.WildFlyElytronBaseProvider.putMakedPasswordImplementations(WildFlyElytronBaseProvider.java:112)
>>>>                 at
>>>>                 org.wildfly.security.WildFlyElytronBaseProvider.putPasswordImplementations(WildFlyElytronBaseProvider.java:107)
>>>>                 at
>>>>                 org.wildfly.security.password.WildFlyElytronPasswordProvider.<init>(WildFlyElytronPasswordProvider.java:43)
>>>>                 at
>>>>                 org.wildfly.security.password.WildFlyElytronPasswordProvider.<clinit>(WildFlyElytronPasswordProvider.java:36)
>>>>                 ... 1 more
>>>>
>>>>                 The exception message is strange because the
>>>>                 handle's method type and the expected type are both
>>>>                 symbolically the same.
>>>>                 I am debugging this exception at the moment.
>>>>                 Thanks,
>>>>                 - Ashutosh Mehra
>>>>
>>>>
>>>>                 On Wed, Sep 11, 2024 at 6:03 AM Andrew Dinn
>>>>                 <adinn at redhat.com> wrote:
>>>>
>>>>                     Oops, sorry, I debugged this a few days ago!
>>>>                     Correction to a few details:
>>>>
>>>>                     On 11/09/2024 10:39, Andrew Dinn wrote:
>>>>                     > A crash due to an NPE was observed in the
>>>>                     Infinispan (Data Grid) server
>>>>                     > app when deployed using the Leyden EA. The
>>>>                     crash still manifests with
>>>>                     > the latest premain code. The crash happens
>>>>                     below an application call
>>>>                     > which employs a method reference as argument
>>>>                     >
>>>>                     >
>>>>                     putMakedPasswordImplementations(this::putService,
>>>>                     this);
>>>>
>>>>                     The called method in turn calls consumer.accept
>>>>
>>>>                                  consumer.accept(new Service(provider,
>>>>                     PASSWORD_FACTORY_TYPE, algorithm,
>>>>                     "org.wildfly.security.password.impl.PasswordFactorySpiImpl",
>>>>                     emptyList,
>>>>                     emptyMap));
>>>>
>>>>                     which enters enters
>>>>                     MethodHandleNative::linkDynamicConstant()
>>>>
>>>>                     > Debugging shows that the call to
>>>>                     linkDynamicConstant() returns null.
>>>>                     >
>>>>                     > A simple reproducer for the problem is
>>>>                     available as a maven project on
>>>>                     > github:
>>>>                     >
>>>>                     >
>>>>                     https://github.com/tristantarrant/elytron-leyden
>>>>                     <https://urldefense.com/v3/__https://github.com/tristantarrant/elytron-leyden__;!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhZaZr1akQ$>
>>>>                     >
>>>>                     > The ReadMe provides an explanation of how to
>>>>                     reproduce the problem. I
>>>>                     > did so and the debugged to find out some of
>>>>                     the details of what is
>>>>                     > happening (see below) but did not fully
>>>>                     clarify the problem. Help from
>>>>                     > someone more conversant with the ins and outs
>>>>                     of method handle
>>>>                     > bootstraps in premain would be welcome.
>>>>                     Details follow.
>>>>                     >
>>>>                     > regards,
>>>>                     >
>>>>                     >
>>>>                     > Andrew Dinn
>>>>                     > -----------
>>>>                     >
>>>>                     > I downloaded the git repo and attached the
>>>>                     Java sources using Maven command
>>>>                     >
>>>>                     >    $ mvn dependency:sources
>>>>                     >
>>>>                     > Having manifested the crash by following the
>>>>                     instructions in the README
>>>>                     > I reran the leyden JVM under gdb using the
>>>>                     following commands to enable
>>>>                     > Java debugging
>>>>                     >
>>>>                     > $ gdb ${LEYDEN_HOME}/bin/java
>>>>                     > (gdb) cd /path/to/mvn/project
>>>>                     > (gdb) run
>>>>                     >
>>>>                     -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005
>>>>
>>>>                     > -classpath
>>>>                     >
>>>>                     /home/adinn/redhat/openjdk/infinispan/elytron-leyden/base/target/elytron-leyden-base-0.0.1-SNAPSHOT.jar:/home/adinn/.m2/repository/org/wildfly/security/wildfly-elytron-credential/2.5.1.
>>>>                     <https://urldefense.com/v3/__http://2.5.1.__;!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhauJ-RjcQ$>Final/wildfly-elytron-credential-2.5.1.Final.jar:/home/adinn/.m2/repository/org/wildfly/security/wildfly-elytron-base/2.5.1.Final/wildfly-elytron-base-2.5.1.Final.jar
>>>>                     -XX:CacheDataStore=elytron.aot
>>>>                     com.redhat.leyden.Main
>>>>                     >
>>>>                     > The problem manifests at
>>>>                     WildflyElytronBaseProvider.java:112 in method
>>>>                     >
>>>>                     WildflyElytronBaseProvider::putMakedPasswordImplementations
>>>>
>>>>                          static void
>>>>                     putMakedPasswordImplementations(Consumer<Service>
>>>>                     consumer, Provider provider) {
>>>>                              for (String algorithm :
>>>>                     MASKED_ALGORITHMS) {
>>>>                                  consumer.accept(new Service(provider,
>>>>                     PASSWORD_FACTORY_TYPE, algorithm,
>>>>                     "org.wildfly.security.password.impl.PasswordFactorySpiImpl",
>>>>                     emptyList,
>>>>                     emptyMap)); <== NPE under this call
>>>>                              }
>>>>
>>>>
>>>>                     > The source code for this method can be found
>>>>                     in the following source jar
>>>>                     >
>>>>                     >
>>>>                     >
>>>>                     ${M2_REPO}/org/wildfly/security/wildfly-elytron-base/2.5.1.Final/wildfly-elytron-base-2.5.1.Final-sources.jar
>>>>                     >
>>>>                     > (where M2_REPO will normally be ~/.m2/repository)
>>>>                     >
>>>>                     > Stepping into accept eventually enters
>>>>                     MethodHandleNative::linkDynamicConstant
>>>>                     > which in turn enters into
>>>>                     ConstantBootstraps.makeConstant(). The caller
>>>>                     > Class at this point is a lambda class which
>>>>                     prints as
>>>>                     >
>>>>                     org.wildfly.security.WildflyElytronBaseProvider$$Lambda/0x800000000c
>>>>                     >
>>>>                     > Several steps further the code enters
>>>>                     BootstrapMethodInvoker::invoke
>>>>                     > (below the app method call but via 3 hidden
>>>>                     frames) with bootstrapMethod
>>>>                     > bound to a DirectMethodHandle. After several
>>>>                     more steps this enters
>>>>                     > DirectMethodHandle$Holder.invokeStatic which
>>>>                     in turn calls
>>>>                     > MethodHandles::classData(Lookup,String,Class).
>>>>                     >
>>>>                     > At this point caller is a MethodHandleLookup
>>>>                     for the lambda class
>>>>                     > Lambda/0x800000000c mentioned above. The
>>>>                     following call
>>>>                     >
>>>>                     >           Object classdata =
>>>>                     classData(caller.lookupClass());
>>>>                     >
>>>>                     > returns null to
>>>>                     DirectMethodHandle$Holder.invokeStatic which
>>>>                     pops the
>>>>                     > same result back out to
>>>>                     BootstrapMethodInvoker::invoke at line 90
>>>>                     >
>>>>                     >                  if (type instanceof Class<?>
>>>>                     c) {
>>>>                     >                      result =
>>>>                     bootstrapMethod.invoke(caller, name, c);
>>>>                     > <== null
>>>>                     >
>>>>                     > This null result pops back out as the value
>>>>                     for the call to
>>>>                     > BootstrapMethodInvoker.invoke(),
>>>>                     ConstantBootstraps.makeConstant() and
>>>>                     > MethodHandleNative::linkDynamicConstant().
>>>>                     >
>>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/leyden-dev/attachments/20240919/b7f017bc/attachment.htm>


More information about the leyden-dev mailing list