Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8319935

Ensure only one JvmtiThreadState is created for one JavaThread associated with attached native thread

    XMLWordPrintable

Details

    • b26
    • generic
    • generic

    Description

      We ran into issues when there are more than one JvmtiThreadState is created for a JavaThread. The problem can lead to memory corruption, which then can manifest into different crashes in completely unrelated area at a later point and make the problem difficult to be diagnosed.

      Following is duplicated from https://bugs.openjdk.org/browse/JDK-8312174?focusedId=14625080&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-14625080:

      The crashes are always due to a bad JavaThread pointer in the current stable ThreadsList::_threads array. Depending on when the corrupted pointer is visited by the VM, it exhibits various different crashes during SafepointSynchronize::synchronize_threads, or ThreadsSMRSupport::free_list, etc. The crashes are latent symptoms caused by random (but not completely random) memory corruption originated from JvmtiEventControllerPrivate::recompute_thread_enabled due to a stale JavaThead (is already exited) referenced in a JvmtiThreadState. For a particular test that I debugged with, the memory corruption always occurs at the 193-th element (a 64-bit word) in the ThreadsList::_threads. All other JavaThread pointers in the array are intact.

      In one of the failed instances that I investigated, the memory address being trashed was 0x00007f9dbc0018b8. The bogus value contained in the memory location was 0x00000000e00f4df0. The debugging information showed there was a JavaThread allocated at 0x00007f9dbc0012b0 earlier during the test execution. The thread later exited and was destroyed. There were two different JvmtiThreadStates were created as associated with this JavaThread. Since the JavaThread was bound to two different JvmtiThreadStates and only one was destroyed during the thread exit. The remaining JvmtiThreadState then contains an invalid pointer (to 0x00007f9dbc0012b0) after the thread exited. At a later point, an array of JavaThread* ( ThreadsList::_threads) was allocated at 0x00007f9dbc0012b0. 0x00007f9dbc0018b8 was a memory location within the array. It appeared that later during JvmtiEventControllerPrivate::recompute_thread_enabled operation, it caused memory trashing at 0x00007f9dbc0018b8 when it encounters the JvmtiThreadState that contains the stale JavaThread* 0x00007f9dbc0012b0.

      Duplicated from https://bugs.openjdk.org/browse/JDK-8312174?focusedId=14625567&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-14625567:

      The two JvmtiThreadStates for one JavaThread issue that we have observed particularly happens for attached native thread. The first creation of a JvmtiThreadState occurs during JvmtiEventControllerPrivate::thread_started when a thread is being attached and it's creating the Java instance of the Thread. The created JvmtiThreadState has a null _thread_oop_h.

        * frame #0: 0x00007f88791948aa libjvm.so`JvmtiThreadState::JvmtiThreadState(JavaThread*, oopDesc*) [inlined] OopHandle::OopHandle(this=0x00005602b76eb810) at oopHandle.hpp:46:17
          frame #1: 0x00007f88791948aa libjvm.so`JvmtiThreadState::JvmtiThreadState(this=0x00005602b76eb800, thread=0x00005602b7769c10, thread_oop=0x0000000000000000) at jvmtiThreadState.cpp:56:19
          frame #2: 0x00007f8879176d83 libjvm.so`JvmtiEventCollector::setup_jvmti_thread_state() [inlined] JvmtiThreadState::state_for_while_locked(thread=<unavailable>, thread_oop=<unavailable>) at jvmtiThreadState.inline.hpp:98:19
          frame #3: 0x00007f8879176cec libjvm.so`JvmtiEventCollector::setup_jvmti_thread_state() at jvmtiThreadState.inline.hpp:111:13
          frame #4: 0x00007f8879176cc4 libjvm.so`JvmtiEventCollector::setup_jvmti_thread_state(this=0x00007ffcbf7bee98) at jvmtiExport.cpp:2953:29
          frame #5: 0x00007f88791773ee libjvm.so`JvmtiSampledObjectAllocEventCollector::start(this=0x00007ffcbf7bee98) at jvmtiExport.cpp:3146:5
          frame #6: 0x00007f887928a549 libjvm.so`MemAllocator::Allocation::notify_allocation_jvmti_sampler() [inlined] JvmtiSampledObjectAllocEventCollector::JvmtiSampledObjectAllocEventCollector(this=0x00007ffcbf7bee98, should_start=true) at jvmtiExport.hpp:560:5
          frame #7: 0x00007f887928a52b libjvm.so`MemAllocator::Allocation::notify_allocation_jvmti_sampler(this=0x00007ffcbf7bef40) at memAllocator.cpp:185:43
          frame #8: 0x00007f887928a94a libjvm.so`MemAllocator::Allocation::notify_allocation(this=<unavailable>, thread=<unavailable>) at memAllocator.cpp:235:3 [artificial]
          frame #9: 0x00007f887928aec9 libjvm.so`MemAllocator::allocate() const [inlined] MemAllocator::Allocation::~Allocation(this=0x00007ffcbf7bef40) at memAllocator.cpp:85:7
          frame #10: 0x00007f887928aeb3 libjvm.so`MemAllocator::allocate(this=0x00007ffcbf7bef90) const at memAllocator.cpp:375:3
          frame #11: 0x00007f8878f2f221 libjvm.so`InstanceKlass::allocate_instance_handle(JavaThread*) [inlined] CollectedHeap::obj_allocate(this=<unavailable>, klass=<unavailable>, size=<unavailable>, __the_thread__=0x00005602b7769c10) at collectedHeap.inline.hpp:36:20
          frame #12: 0x00007f8878f2f1fd libjvm.so`InstanceKlass::allocate_instance_handle(JavaThread*) [inlined] InstanceKlass::allocate_instance(this=<unavailable>, __the_thread__=0x00005602b7769c10) at instanceKlass.cpp:1442:38
          frame #13: 0x00007f8878f2f1ee libjvm.so`InstanceKlass::allocate_instance_handle(this=<unavailable>, __the_thread__=<unavailable>) at instanceKlass.cpp:1462:33
          frame #14: 0x00007f8878f6bff9 libjvm.so`JavaThread::allocate_threadObj(this=0x00005602b7769c10, thread_group=Handle @ r13, thread_name=<unavailable>, daemon=false, __the_thread__=<unavailable>) at javaThread.cpp:222:35
          frame #15: 0x00007f8879000020 libjvm.so`attach_current_thread(vm=<unavailable>, penv=0x00007ffcbf7bf1c0, _args=<unavailable>, daemon=<unavailable>) at jni.cpp:3819:13
      ...

      The second creation of JvmtiThreadState for the same JavaThread (for the attaching native thread) occurs during JvmtiExport::post_thread_start when attach:

        * frame #0: 0x00007f88791948aa libjvm.so`JvmtiThreadState::JvmtiThreadState(JavaThread*, oopDesc*) [inlined] OopHandle::OopHandle(this=0x00005602b76eb910) at oopHandle.hpp:46:17
          frame #1: 0x00007f88791948aa libjvm.so`JvmtiThreadState::JvmtiThreadState(this=0x00005602b76eb900, thread=0x00005602b7769c10, thread_oop=0x0000000116000000) at jvmtiThreadState.cpp:56:19
          frame #2: 0x00007f8879166936 libjvm.so`JvmtiEventControllerPrivate::thread_started(JavaThread*) [inlined] JvmtiThreadState::state_for_while_locked(thread=0x00005602b7769c10, thread_oop=<unavailable>) at jvmtiThreadState.inline.hpp:98:19
          frame #3: 0x00007f88791668b3 libjvm.so`JvmtiEventControllerPrivate::thread_started(thread=0x00005602b7769c10) at jvmtiEventController.cpp:744:31
          frame #4: 0x00007f887916c1f4 libjvm.so`JvmtiExport::post_thread_start(thread=0x00005602b7769c10) at jvmtiExport.cpp:1476:3
          frame #5: 0x00007f88790000af libjvm.so`attach_current_thread(vm=<unavailable>, penv=0x00007ffcbf7bf1c0, _args=<unavailable>, daemon=<unavailable>) at jni.cpp:3849:5
      ...

      Before the https://git.openjdk.org/jdk/commit/fda142ff6cfefa12ec1ea4d4eb48b3c1b285bc04 change, JvmtiEventControllerPrivate::thread_started calls JvmtiThreadState::state_for_while_locked directly, which actually finds the already created JvmtiThreadState from the JavaThread. However the 'if (state == nullptr || state->get_thread_oop() != thread_oop)' check fails since the thread_oop from the state is null and is not the same as the actual thread_oop. Since the check fails, it decides to create a new JvmtiThreadState for the JavaThread.

      https://git.openjdk.org/jdk/commit/fda142ff6cfefa12ec1ea4d4eb48b3c1b285bc04 changed to call JvmtiThreadState::state_for(thread) in JvmtiEventControllerPrivate::thread_started. JvmtiThreadState::state_for(thread) gets the JvmtiThreadState from the JavaThread and just returns. Hence there's no additional state being created here.

      https://git.openjdk.org/jdk/commit/fda142ff6cfefa12ec1ea4d4eb48b3c1b285bc04 resolved the issue by luck, I think we should put additional reinforcement. Hence filing this bug.

      Attachments

        Issue Links

          Activity

            People

              jiangli Jiangli Zhou
              jiangli Jiangli Zhou
              Votes:
              0 Vote for this issue
              Watchers:
              8 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: