JDK-8137263 : [JVMCI] CompilerToVM::getResolvedJavaMethod has a crash when providing HotSpotResolvedJavaMethodImpl as base
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 9
  • Priority: P2
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2015-09-28
  • Updated: 2023-07-21
  • Resolved: 2023-07-21
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9Resolved
Related Reports
Duplicate :  
Description
Try to provide valid instance of HotSpotResolvedJavaMethodImpl to CompilerToVM::getResolvedJavaMethod with offset set to 0. It'll lead to a crash:

assert(((Metadata*)obj)->is_valid()) failed: obj is valid

Stack: [0x00007f778d1e6000,0x00007f778d2e7000],  sp=0x00007f778d2e4880,  free space=1018k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0x121a981]  VMError::report_and_die()+0x151
V  [libjvm.so+0x84c3fb]  report_vm_error(char const*, int, char const*, char const*)+0x7b
V  [libjvm.so+0x4ac7ef]  methodHandle::methodHandle(Method*)+0xbf
V  [libjvm.so+0xc369c9]  c2v_getResolvedJavaMethod(JNIEnv_*, _jobject*, _jobject*, long)+0x619
...

an test example to reproduce problem and hs_err are attached


Also, probably related issue is that externally setting Method** raw pointer to HotSpotResolvedJavaMethodImpl and providing it to getResolvedJavaMethod(uncomment line 85-93 in attached source to enable this logic) will make it return null value.
Comments
Ok, what's the point of separate handling of HotSpotResolvedJavaMethodImpl then? Both javadoc and C++ code has a separate usecase with HotSpotResolvedJavaMethodImpl object passed as base, so, what's the point of using it? It appears then, that metaspaceMethod field of HotSpotResolvedJavaMethodImpl, which keeps Method* is used to be changed by some offset to get correct Method** ? So, it's kind of Method* + someConstant = Method**, which seems totally wrong(surely, physically possible). Is it the main and only supposed usage of such case(i.e. passing HotSpotResolvedJavaMethodImpl as base object)? In case it is, i think a javadoc must be updated to reflect it
01-10-2015

The addition of base + displacement isn't supposed to produce a valid Method*. That would be useless since the only value you could ever pass for displacement would be 0 and you'd be returning the same object you passed in. It's for reading Method* objects out of C++ objects so it's supposed to produce a pointer to a valid Method*. The code as written is doing exactly what it's supposed to.
01-10-2015

but in this particular test, a valid HotSpotResolvedJavaMethodImpl is passed into getResolvedJavaMethod as base. After that, a getResolvedJavaMethod method body just getting metaspaceMethod member(which is valid Method*) and an offset(which is 0 in this case) is added, making result a valid Method* pointer(which is the same as in original valid HotSpotResolvedJavaMethodImpl, ($validPointer + 0 = $validPointer) so, it's obviously valid). And then(in a current code) it's treated as Method** and dereferenced, which seems to be not correct. Alternatively, a javadoc can be modified to reflect, that in case of HotSpotResolvedJavaMethodImpl, a metaspace pointer is treated as pointer to metaspace pointer
01-10-2015

I think you misunderstand what this method does. It reads a location from memory that is supposed to be a Method* and hands back the HotSpotResolvedJavaMethodImpl wrapping that nmethod. So you can't just pass in any old object and offset and expect it to work. Maybe the Java doc language isn't explicit enough? In your test you're asking it to read some location from a Method* and hand back the HotSpotResolvedJavaMethodImpl representing that Method*, so if you don't pass an offset that points at a Method* then it will crash.
01-10-2015

ILW=HML=>P2
01-10-2015

Seems like it's not a test problem. According to javadoc for CompilerToVM::getResolvedJavaMethod: * If the {@code base} object is a * {@link HotSpotResolvedJavaMethodImpl}, {@link HotSpotConstantPool} or * {@link HotSpotResolvedObjectTypeImpl} then the metaspace pointer is fetched from that object * and used as the base. Otherwise the object itself is used as the base. I've looked into getResolvedJavaMethod cpp code and found, that in case base object is HotSpotResolvedJavaMethodImp, a following code is executed: ... if (base_object->is_a(SystemDictionary::HotSpotResolvedJavaMethodImpl_klass())) { method = *((Method**)(HotSpotResolvedJavaMethodImpl::metaspaceMethod(base_object) + offset)); } so, a metaspaceMethod pointer(Method*) is fetched from HotSpotResolvedJavaMethodImpl, an offset is added and then, there is a cast to Method**, which is kind of strange and seems not to be according to javadoc behaviour. Then, this pointer is dereferenced and result is of type Method*, which is assigned to Method variable. So, vm is crashed in overloaded operator= code (on assert(_value->is_valid(), "obj is valid") : handles.inline.hpp:line 92). I've changed this line: method = *((Method**)(HotSpotResolvedJavaMethodImpl::metaspaceMethod(base_object) + offset)); to: method = (Method*)(HotSpotResolvedJavaMethodImpl::metaspaceMethod(base_object) + offset)); rebuilt jdk and it seems to work correctly. Please confirm if these findings are correct/incorrect.
01-10-2015

Please fix the test.
28-09-2015

So the first test misunderstands how this API works. A HotSpotResolvedJavaMethodImpl is a reflective wrapper around a Method*. So a read from a HotSpotResolvedJavaMethodImpl is really a read from the Method* itself. So you can't actually use it to introspect on HotSpotResolvedJavaMethodImpl which is the first test is doing. It will always be translated into Method* + offset. So generally speaking every test in the this test case is incorrect. Generally speaking while it's possible to call this method on HotSpotResolvedJavaMethodImpl there are no places where the VM stores a Method* inside of a Method*. So it will never do anything useful for that type, apart from return NULL or crash. I think in the current system only HotSpotResolvedObjectTypeImpl which wraps Klass* will ever contains Method*. This method was written as a replacement for direct use of Unsafe so it was intended to be agnostic about what types and values are permitted and just handle all metaspace wrappers in a uniform way. Knowledge about the VM structure and offsets at a higher level controls the usage of this API.
28-09-2015

An obvious problem in the test itself is that getPtrToMethod is returning the value from GetResolvedJavaTypeTest, which I assume isn't actually a Method*. private static long getPtrToMethod() { Field field; try { field = GetResolvedJavaTypeTest.class.getDeclaredField("PTR"); What is the test that's doing "ptr / 2L" trying to do? If you pass bogus values through this interface it will crash. You shouldn't be using a constant for the field offset either. Use UNSAFE.objectFieldOffset. I'll look some more.
28-09-2015