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

JVM crash on watched field access from native code

XMLWordPrintable

    • generic
    • generic

      ADDITIONAL SYSTEM INFORMATION :
      The production case happened on CentOS 6.10 x86_64 and Oracle Java 1.8.0_172-b11, and persisted with the latest OpenJDK Java8 update (212-b04).
      The reproducer reproduced the issue on Fedora 30 and CentOS 7.6, using OpenJDK build 1.8.0_212-b04

      A DESCRIPTION OF THE PROBLEM :
      When a JVMTI agent is being used to instrument the system, a crash occurs if a watched field is being accessed by native code (e.g. via jni->GetObjectField()) if the top Java frame is JIT-compiled.

      It seems like while the HotSpot JVM in general tries to work in interpreted mode if fields are being watched, it cannot convert a Java frame calling native code into an interpreted frame and so if a watch is being installed while the JVM is running, such a frame would remain JIT-compiled, and a field access by the native code would trigger an assertion.

      In particular, if a thread is blocking waiting for a new connection to be accepted, the thread is sleeping in native function java.net.PlainSocketImpl.socketAccept(). Suppose the Java parts that called it were JIT compiled. As the thread is sleeping, a watch is placed on java.net.SocketImpl.fd field. Then a connection arrives, and java.net.PlainSocketImpl.socketAccept() accesses java.net.SocketImpl.fd before returning to its caller, which triggers the assertion.

      A sample stack trace from the hs_err_pidXXX.log file is:
      Stack: [0x00007fe8ebcfd000,0x00007fe8ebdfe000], sp=0x00007fe8ebdfc160, free space=1020k
      Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
      V [libjvm.so+0x592d38] frame::interpreter_frame_bcp() const+0x8
      V [libjvm.so+0x7a6495] JvmtiExport::post_field_access_by_jni(JavaThread*, oopDesc*, Klass*, _jfieldID*, bool)+0x1a5
      V [libjvm.so+0x7a6651] JvmtiExport::jni_GetField_probe(JavaThread*, _jobject*, oopDesc*, Klass*, _jfieldID*, bool)+0x41
      V [libjvm.so+0x6f397c] jni_GetObjectField+0x23c
      C [libnet.so+0xd78b] Java_java_net_PlainSocketImpl_socketAccept+0x2cb
      J 23818 java.net.PlainSocketImpl.socketAccept(Ljava/net/SocketImpl;)V (0 bytes) @ 0x00007fe9a106514c [0x00007fe9a1065080+0xcc]
      j java.net.AbstractPlainSocketImpl.accept(Ljava/net/SocketImpl;)V+7
      j java.net.ServerSocket.implAccept(Ljava/net/Socket;)V+60
      j java.net.ServerSocket.accept()Ljava/net/Socket;+48
      j org.apache.tomcat.util.net.DefaultServerSocketFactory.acceptSocket(Ljava/net/ServerSocket;)Ljava/net/Socket;+1
      j org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run()V+95
      j java.lang.Thread.run()V+11

      A stack trace from the core dump is:
      #0 0x00007efe477eb4f5 in raise () from /lib64/libc.so.6
      #1 0x00007efe477eccd5 in abort () from /lib64/libc.so.6
      #2 0x00007efe471097e5 in os::abort (dump_core=true) at /home/centos/jdk8u/hotspot/src/os/linux/vm/os_linux.cpp:1569
      #3 0x00007efe4729d083 in VMError::report_and_die (this=0x7efd9d6f2c60) at /home/centos/jdk8u/hotspot/src/share/vm/utilities/vmError.cpp:1107
      #4 0x00007efe47110312 in JVM_handle_linux_signal (sig=11, info=0x7efd9d6f2e70, ucVoid=0x7efd9d6f2d40, abort_if_unrecognized=-1653658656)
          at /home/centos/jdk8u/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp:541
      #5 0x00007efe47105633 in signalHandler (sig=11, info=0x7efd9d6f2e70, uc=0x7efd9d6f2d40) at /home/centos/jdk8u/hotspot/src/os/linux/vm/os_linux.cpp:4542
      #6 <signal handler called>
      #7 interpreter_frame_bcx (this=0x7efd9d6f3330) at /home/centos/jdk8u/hotspot/src/share/vm/runtime/frame.hpp:255
      #8 frame::interpreter_frame_bcp (this=0x7efd9d6f3330) at /home/centos/jdk8u/hotspot/src/share/vm/runtime/frame.cpp:463
      #9 0x00007efe46f9313d in JvmtiExport::post_field_access_by_jni (thread=0x7efe4099f800, obj=<value optimized out>, klass=0x7e0946140, fieldID=0x72, is_static=<value optimized out>) at /home/centos/jdk8u/hotspot/src/share/vm/prims/jvmtiExport.cpp:1486
      #10 0x00007efe46f932e0 in JvmtiExport::jni_GetField_probe (thread=<value optimized out>, jobj=0x7efd9d6f3598, obj=0x77660d7f8, klass=<value optimized out>, fieldID=<value optimized out>, is_static=<value optimized out>) at /home/centos/jdk8u/hotspot/src/share/vm/prims/jvmtiExport.cpp:1437
      #11 0x00007efe46ee1ad0 in jni_GetObjectField (env=0x7efe4099f9e0, obj=0x7efd9d6f3598, fieldID=0x72) at /home/centos/jdk8u/hotspot/src/share/vm/prims/jni.cpp:2624
      #12 0x00007efe29fd7edb in Java_java_net_PlainSocketImpl_socketAccept (env=0x7efe4099f9e0, this=0x7efd9d6f3590, socket=0x7efd9d6f3598) at /home/centos/jdk8u/jdk/src/solaris/native/java/net/PlainSocketImpl.c:790

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      A stripped-down code sample is available usiat git clone https://uris58@bitbucket.org/uris58/crashdemo.git
      It includes the source code for a simple native agent and a small Gradle project for a Java application that triggers the issue.

      The README.md includes instructions, repeated here:
      1. build the native agent
      2. build the app (gradle jar)
      3. run the app with the agent:
      java -agentpath:/path/to/libNativeAgent.so -jar /path/to/crashdemo-1.0.0-SNAPSHOT.jar

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Program runs and terminates within a few seconds
      ACTUAL -
      The JVM crashes

      ---------- BEGIN SOURCE ----------
      A git repository with native and Java code is at:
      https://uris58@bitbucket.org/uris58/crashdemo.git

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      The following patch seems to avoid the issue by not processing the field event in this case (I hope it is not clobbered by the bug system):

      diff -r b2000ea410b0 src/share/vm/prims/jvmtiExport.cpp
      --- a/src/share/vm/prims/jvmtiExport.cpp Wed Apr 10 11:38:47 2019 +0200
      +++ b/src/share/vm/prims/jvmtiExport.cpp Tue May 21 18:24:40 2019 +0000
      @@ -1472,6 +1472,9 @@
         // field accesses are not watched so bail
         if (!fd.is_field_access_watched()) return;
       
      + // left-over compiled frame so bail
      + if (!thread->last_frame().is_interpreted_frame()) return;
      +
         HandleMark hm(thread);
         KlassHandle h_klass(thread, klass);
         Handle h_obj;
      @@ -1569,6 +1572,9 @@
         // field modifications are not watched so bail
         if (!fd.is_field_modification_watched()) return;
       
      + // left-over compiled frame so bail
      + if (!thread->last_frame().is_interpreted_frame()) return;
      +
         HandleMark hm(thread);
       
         Handle h_obj;

      FREQUENCY : always


            lmesnik Leonid Mesnik
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated: