JDK-8233839 : aarch64: missing memory barrier in NewObjectArrayStub and NewTypeArrayStub
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 8-aarch64,11,14
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: aarch64
  • Submitted: 2019-11-08
  • Updated: 2023-07-21
  • Resolved: 2019-11-09
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 11 JDK 13 JDK 14 Other
11.0.6Fixed 13.0.4Fixed 14 b23Fixed openjdk8u292Fixed
Related Reports
Duplicate :  
Relates :  
Description
I witnessed random fail of one jcstress test on my 128-core aarch64 server: "org.openjdk.jcstress.tests.defaultValues.arrays.small.plain.StringTest" 
I used the latest aarch64 jdk8u release build. 
Command line: java -jar /home/yangfei/tools/jcstress-tests-all-20191016.jar -jvmArgs "-XX:TieredStopAtLevel=1"  -t "org.openjdk.jcstress.tests.defaultValues.arrays.small.plain.StringTest" -time 5000
Error messages: 
   [ERROR] o.o.j.t.defaultValues.arrays.small.plain.StringTest
    Messages:
        Unrecoverable error while running
        java.lang.ArrayIndexOutOfBoundsException: 0
        at org.openjdk.jcstress.tests.defaultValues.arrays.small.plain.StringTest.actor2(StringTest.java:55)
        at org.openjdk.jcstress.tests.defaultValues.arrays.small.plain.StringTest_jcstress.actor2(StringTest_jcstress.java:193)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

The jcsress test looks like: 

@JCStressTest
@Outcome.Outcomes({@Outcome(
   id = {"null, null, null, null"},
   expect = Expect.ACCEPTABLE,
   desc = "Default value for the element."
), @Outcome(
   expect = Expect.FORBIDDEN,
   desc = "Non-default values are forbidden."
)})
@State
public class StringTest {
   String[] arr;

   @Actor
   public void actor1() {
      this.arr = new String[4];
   }

   @Actor
   public void actor2(LLLL_Result r) {
      String[] a = this.arr;
      if (a == null) {
         r.r1 = r.r2 = r.r3 = r.r4 = null;
      } else {
         r.r1 = a[0];    <==== throws exception here
         r.r2 = a[1];
         r.r3 = a[2];
         r.r4 = a[3];
      }

   }
}

From the error message, looks like we are using an array before the initialization of it finished.  
I even reproduced the random failure with a similar case:  

public class ThreadTest {
        public static String[] arr;
        public static String r1, r2, r3, r4;

        static class FirstThread extends Thread {
                public void actor1() {
                        ThreadTest.arr = new String[4];
                }

                public void run() {
                    while (true) {
                        actor1();
                    }
                }
        }

        static class SecondThread extends Thread {
                public void actor2() {
                        String[] a = ThreadTest.arr;
                        if (a == null) {
                                ThreadTest.r1 = null;
                                ThreadTest.r2 = null;
                                ThreadTest.r3 = null;
                                ThreadTest.r4 = null;
                        } else {
                                ThreadTest.r1 = a[0];
                                ThreadTest.r2 = a[1];
                                ThreadTest.r3 = a[2];
                                ThreadTest.r4 = a[3];
                        }
                }

                public void run() {
                        while (true) {
                                actor2();
                        }
                }
        }

        public static void main(String args[]) {
                FirstThread t1 = new FirstThread();
                SecondThread t2 = new SecondThread();
                t1.start();
                t2.start();
                try {
                        t1.join();
                        t2.join();
                } catch (InterruptedException e) {
                        System.out.println("INTERRUPTED_MESSAGE");
                }

                System.out.println("PASS");
        }

}

Command line: java -XX:TieredStopAtLevel=1 ThreadTest
Error message: 
Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 0
        at ThreadTest$SecondThread.actor2(ThreadTest.java:26)
        at ThreadTest$SecondThread.run(ThreadTest.java:35)

JIT code for actor1:

Compiled method (c1)     310   16       1       ThreadTest$FirstThread::actor1 (8 bytes)
 total in heap  [0x0000ffff904cbc50,0x0000ffff904cbff8] = 936
 relocation     [0x0000ffff904cbd70,0x0000ffff904cbd88] = 24
 main code      [0x0000ffff904cbdc0,0x0000ffff904cbf40] = 384
 stub code      [0x0000ffff904cbf40,0x0000ffff904cbf50] = 16
 oops           [0x0000ffff904cbf50,0x0000ffff904cbf60] = 16
 metadata       [0x0000ffff904cbf60,0x0000ffff904cbf70] = 16
 scopes data    [0x0000ffff904cbf70,0x0000ffff904cbf90] = 32
 scopes pcs     [0x0000ffff904cbf90,0x0000ffff904cbff0] = 96
 dependencies   [0x0000ffff904cbff0,0x0000ffff904cbff8] = 8
Loaded disassembler from /home/yangfei/jdk8u-release/jvm/openjdk-1.8.0-internal/jre/lib/aarch64/server/hsdis-aarch64.so
Decoding compiled method 0x0000ffff904cbc50:
Code:
[Disassembling for mach='aarch64']
[Entry Point]
[Constants]
  # {method} {0x0000ffff81b57880} 'actor1' '()V' in 'ThreadTest$FirstThread'
  #           [sp+0x40]  (sp of caller)
  0x0000ffff904cbdc0: ldr       w8, [x1,#8]
  0x0000ffff904cbdc4: cmp       w9, w8
  0x0000ffff904cbdc8: b.eq      0x0000ffff904cbe00
  0x0000ffff904cbdcc: b 0x0000ffff900b1ec0      ;   {runtime_call}
  0x0000ffff904cbdd0: nop
  0x0000ffff904cbdd4: nop
  0x0000ffff904cbdd8: nop
  0x0000ffff904cbddc: nop
  0x0000ffff904cbde0: nop
  0x0000ffff904cbde4: nop
  0x0000ffff904cbde8: nop
  0x0000ffff904cbdec: nop
  0x0000ffff904cbdf0: nop
  0x0000ffff904cbdf4: nop
  0x0000ffff904cbdf8: nop
  0x0000ffff904cbdfc: nop
[Verified Entry Point]
  0x0000ffff904cbe00: nop
  0x0000ffff904cbe04: orr       x9, xzr, #0xffffffffffffc000
  0x0000ffff904cbe08: str       xzr, [sp,x9]
  0x0000ffff904cbe0c: sub       sp, sp, #0x40
  0x0000ffff904cbe10: stp       x29, x30, [sp,#48]  ;*iconst_4
                                                ; - ThreadTest$FirstThread::actor1@0 (line 7)

  0x0000ffff904cbe14: orr       w19, wzr, #0x4
  0x0000ffff904cbe18: mov       x3, #0xaf08                     // #44808
                                                ;   {metadata('java/lang/String'[])}
  0x0000ffff904cbe1c: movk      x3, #0x1, lsl #16
  0x0000ffff904cbe20: movk      x3, #0x8, lsl #32
  0x0000ffff904cbe24: ubfx      x19, x19, #0, #32
  0x0000ffff904cbe28: mov       x5, x19
  0x0000ffff904cbe2c: mov       x8, #0xffffff                   // #16777215
  0x0000ffff904cbe30: cmp       x19, x8
  0x0000ffff904cbe34: b.cs      0x0000ffff904cbf0c
  0x0000ffff904cbe38: mov       x4, #0x17                       // #23
  0x0000ffff904cbe3c: add       x4, x4, w19, uxtw #2
  0x0000ffff904cbe40: and       x4, x4, #0xfffffffffffffff8
  0x0000ffff904cbe44: ldr       x0, [x28,#96]
  0x0000ffff904cbe48: add       x4, x0, x4, uxtx
  0x0000ffff904cbe4c: ldr       x8, [x28,#112]
  0x0000ffff904cbe50: cmp       x4, x8
  0x0000ffff904cbe54: b.hi      0x0000ffff904cbf0c
  0x0000ffff904cbe58: str       x4, [x28,#96]
  0x0000ffff904cbe5c: sub       x4, x4, x0
  0x0000ffff904cbe60: orr       x2, xzr, #0x1
  0x0000ffff904cbe64: str       x2, [x0]
  0x0000ffff904cbe68: eor       x2, x3, #0x800000000
  0x0000ffff904cbe6c: str       w2, [x0,#8]
  0x0000ffff904cbe70: str       w19, [x0,#12]
  0x0000ffff904cbe74: subs      x4, x4, #0x10
  0x0000ffff904cbe78: b.eq      0x0000ffff904cbecc
  0x0000ffff904cbe7c: add       x0, x0, #0x10
  0x0000ffff904cbe80: lsr       x4, x4, #3
  0x0000ffff904cbe84: and       x8, x4, #0x7
  0x0000ffff904cbe88: sub       x4, x4, x8
  0x0000ffff904cbe8c: add       x19, x0, x8, lsl #3
  0x0000ffff904cbe90: adr       x9, 0x0000ffff904cbec0
  0x0000ffff904cbe94: sub       x9, x9, x8, lsl #2
  0x0000ffff904cbe98: br        x9
  0x0000ffff904cbe9c: sub       x4, x4, #0x8
  0x0000ffff904cbea0: str       xzr, [x19,#-64]
  0x0000ffff904cbea4: str       xzr, [x19,#-56]
  0x0000ffff904cbea8: str       xzr, [x19,#-48]
  0x0000ffff904cbeac: str       xzr, [x19,#-40]
  0x0000ffff904cbeb0: str       xzr, [x19,#-32]
  0x0000ffff904cbeb4: str       xzr, [x19,#-24]
  0x0000ffff904cbeb8: str       xzr, [x19,#-16]
  0x0000ffff904cbebc: str       xzr, [x19,#-8]
  0x0000ffff904cbec0: add       x19, x19, #0x40
  0x0000ffff904cbec4: cbnz      x4, 0x0000ffff904cbe9c
  0x0000ffff904cbec8: sub       x0, x0, #0x10
  0x0000ffff904cbecc: dmb       ishst           ;*anewarray
                                                ; - ThreadTest$FirstThread::actor1@1 (line 7)

  0x0000ffff904cbed0: mov       x1, #0x22c0                     // #8896
                                                ;   {oop(a 'java/lang/Class' = 'ThreadTest')}
  0x0000ffff904cbed4: movk      x1, #0x8016, lsl #16
  0x0000ffff904cbed8: movk      x1, #0x5, lsl #32
  0x0000ffff904cbedc: lsr       x8, x0, #3
  0x0000ffff904cbee0: str       w8, [x1,#104]
  0x0000ffff904cbee4: lsr       x0, x1, #9
  0x0000ffff904cbee8: mov       x1, #0xf000                     // #61440
  0x0000ffff904cbeec: movk      x1, #0x8bff, lsl #16
  0x0000ffff904cbef0: movk      x1, #0xffff, lsl #32
  0x0000ffff904cbef4: strb      wzr, [x0,x1,lsl #0]  ;*putstatic arr
                                                ; - ThreadTest$FirstThread::actor1@4 (line 7)

  0x0000ffff904cbef8: ldp       x29, x30, [sp,#48]
  0x0000ffff904cbefc: add       sp, sp, #0x40
  0x0000ffff904cbf00: adrp      x8, 0x0000ffffa02e3000
                                                ;   {poll_return}
  0x0000ffff904cbf04: ldr       wzr, [x8]       ;   {poll_return}
  0x0000ffff904cbf08: ret
  0x0000ffff904cbf0c: bl        0x0000ffff900dcf80  ; OopMap{off=336}              < ===================  slow path
                                                ;*anewarray
                                                ; - ThreadTest$FirstThread::actor1@1 (line 7)
                                                ;   {runtime_call}
  0x0000ffff904cbf10: b 0x0000ffff904cbed0
  0x0000ffff904cbf14: nop
  0x0000ffff904cbf18: nop
  0x0000ffff904cbf1c: ldr       x0, [x28,#784]
  0x0000ffff904cbf20: str       xzr, [x28,#784]
  0x0000ffff904cbf24: str       xzr, [x28,#792]
  0x0000ffff904cbf28: ldp       x29, x30, [sp,#48]
  0x0000ffff904cbf2c: add       sp, sp, #0x40
  0x0000ffff904cbf30: b 0x0000ffff900defc0      ;   {runtime_call}
  0x0000ffff904cbf34: .inst     0x00000000 ; undefined
  0x0000ffff904cbf38: .inst     0x00000000 ; undefined
  0x0000ffff904cbf3c: .inst     0x00000000 ; undefined
[Exception Handler]
[Stub Code]
  0x0000ffff904cbf40: bl        0x0000ffff900dc8c0  ;   {no_reloc}
[Deopt Handler Code]
  0x0000ffff904cbf44: adr       x30, 0x0000ffff904cbf44
  0x0000ffff904cbf48: b 0x0000ffff900b35c0      ;   {runtime_call}
  0x0000ffff904cbf4c: .inst     0x00000000 ; undefined
OopMapSet contains 1 OopMaps

#0
OopMap{off=336}

As there is a StoreStore memory barrier for the fast path, I looks into the slow path here.  
Obviously, the StoreStore memory barrier is missing for the slow path.  


Patch for aarch64 8u: 
diff -r 02145a45eff6 src/cpu/aarch64/vm/c1_Runtime1_aarch64.cpp
--- a/src/cpu/aarch64/vm/c1_Runtime1_aarch64.cpp        Mon Nov 05 12:53:55 2018 +0100
+++ b/src/cpu/aarch64/vm/c1_Runtime1_aarch64.cpp        Fri Nov 08 11:05:48 2019 +0800
@@ -877,6 +877,7 @@
           __ sub(arr_size, arr_size, t1);  // body length
           __ add(t1, t1, obj);       // body start
           __ initialize_body(t1, arr_size, 0, t2);
+          __ membar(Assembler::StoreStore);
           __ verify_oop(obj);

           __ ret(lr);
@@ -905,6 +906,7 @@
           __ sub(arr_size, arr_size, t1);  // body length
           __ add(t1, t1, obj);       // body start
           __ initialize_body(t1, arr_size, 0, t2);
+          __ membar(Assembler::StoreStore);
           __ verify_oop(obj);

           __ ret(lr);


Patch for 14 and 11u: 
diff -r ad157fab6bf5 src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp   Thu Nov 07 16:26:57 2019 -0800
+++ b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp   Fri Nov 08 16:10:08 2019 +0800
@@ -840,6 +840,7 @@
           __ sub(arr_size, arr_size, t1);  // body length
           __ add(t1, t1, obj);       // body start
           __ initialize_body(t1, arr_size, 0, t2);
+          __ membar(Assembler::StoreStore);
           __ verify_oop(obj);

           __ ret(lr);

Comments
Fix Request for 13u. The patch seems critical to have on 13u. The patch applies cleanly to jdk13u-dev and pass tier1.
26-05-2020

This is not for upstream jdk8u, but the AArch64 8u tree, so please do not tag it with jdk8u-fix-request. If you want to use a similar approval system for the AArch64 8u tree, I suggest something along the lines of jdk8u-aarch64-fix-request.
13-12-2019

This was backported to 8u via https://hg.openjdk.java.net/aarch64-port/jdk8u-shenandoah/hotspot/rev/09d4b646f756.
04-12-2019

11u Fix Request Backporting this patch to fix one aarch64 C1 runtime bug that happens randomly and is hard to reproduce. Patch applies cleanly to 11u-dev repo. Tier1 tests pass with the patch.
11-11-2019

URL: https://hg.openjdk.java.net/jdk/jdk/rev/90cf1d4e712f User: fyang Date: 2019-11-09 00:26:17 +0000
09-11-2019