RFR: 8313816: Accessing jmethodID might lead to spurious crashes
Coleen Phillimore
coleenp at openjdk.org
Mon Nov 20 22:12:09 UTC 2023
On Tue, 14 Nov 2023 17:56:09 GMT, Jaroslav Bachorik <jbachorik at openjdk.org> wrote:
> Please, review this fix for a corner case handling of `jmethodID` values.
>
> The issue is related to the interplay between `jmethodID` values and method redefinitions. Each `jmethodID` value is effectively a pointer to a `Method` instance. Once that method gets redefined, the `jmethodID` is updated to point to the last `Method` version.
> Unless the method is still on stack/running, in which case the original `jmethodID` will be redirected to the latest `Method` version and at the same time the 'previous' `Method` version will receive a new `jmethodID` pointing to that previous version.
>
> If we happen to capture stacktrace via `GetStackTrace` or `GetAllStackTraces` JVMTI calls while this previous `Method` version is still on stack we will have the corresponding frame identified by a `jmethodID` pointing to that version.
> However, sooner or later the 'previous' class version becomes eligible for cleanup at what time all contained `Method` instances. The cleanup process will not perform the `jmethodID` pointer maintenance and we will end up with pointers to deallocated memory.
> This is caused by the fact that the `jmethodID` lifecycle is bound to `ClassLoaderData` instance and all relevant `jmethodID`s will get batch-updated when the class loader is being released and all its classes are getting unloaded.
>
> This means that we need to make sure that if a `Method` instance is being deallocate the associated `jmethodID` (if any) must not point to the deallocated instance once we are finished. Unfortunately, we can not just update the `jmethodID` values in bulk when purging an old class version - the per `InstanceKlass` jmethodID cache is present only for the main class version and contains `jmethodID` values for both the old and current method versions.
>
> Therefore we need to perform `jmethodID` lookup when we are about to deallocate a `Method` instance and clean up the pointer only if that `jmethodID` is pointing to the `Method` instance which is being deallocated.
>
> _(For anyone interested, a much lengthier writeup is available in [my blog](https://jbachorik.github.io/posts/mysterious-jmethodid))_
Good analysis for a very subtle bug. I have a couple of comments, and maybe the test can be simplified but approving the change.
src/hotspot/share/classfile/classFileParser.cpp line 5579:
> 5577:
> 5578: if (_methods != nullptr) {
> 5579: // Free methods - those methods are not fully wired and miss the method holder
How about saying: for methods whose InstanceKlass as method holder is not yet created?
test/hotspot/jtreg/serviceability/jvmti/thread/GetStackTrace/GetStackTraceAndRetransformTest/GetStackTraceAndRetransformTest.java line 53:
> 51: import java.util.List;
> 52: import java.util.concurrent.CyclicBarrier;
> 53: import java.util.concurrent.locks.LockSupport;
Do you need all these imports?
There's a simple RedefineClassHelper class that does most of the work, but maybe you need the explicit agent code to reproduce the crash? See test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineRunningMethodsWithBacktrace.java as an example.
-------------
Marked as reviewed by coleenp (Reviewer).
PR Review: https://git.openjdk.org/jdk/pull/16662#pullrequestreview-1740746213
PR Review Comment: https://git.openjdk.org/jdk/pull/16662#discussion_r1399811821
PR Review Comment: https://git.openjdk.org/jdk/pull/16662#discussion_r1399810008
More information about the hotspot-dev
mailing list