[aarch64-port-dev ] AArch64: Incorrect matching rule leading to improper oop instruction encoding

wangyadong (E) yadonn.wang at huawei.com
Sun Jul 6 11:46:30 UTC 2025


Hi, guys,

We have found a suspected C2 bug of Aarch64 port, randomly crashed in the arm64 server, and existed since day one from the initial commit and on all Java 8+ versions.

C2 uses JNI handles as placeholders to encoding constant oops, and one of some handle maybe locate at the address of byte_map_base, which is not memory reserved by CardTableModRefBS byte_map.

  // The assember store_check code will do an unsigned shift of the oop,
  // then add it to byte_map_base, i.e.
  //
  //   _byte_map = byte_map_base + (uintptr_t(low_bound) >> card_shift)
  _byte_map = (jbyte*) heap_rs.base();
  byte_map_base = _byte_map - (uintptr_t(low_bound) >> card_shift);


C2 will incorrectly match ConP of oop to ConP of byte_map_base in aarch64.ad.

// Card Table Byte Map Base
operand immByteMapBase()
%{
  // Get base of card map
  predicate((jbyte*)n->get_ptr() ==
        ((CardTableModRefBS*)(Universe::heap()->barrier_set()))->byte_map_base);
  match(ConP);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Load Byte Map Base Constant
instruct loadByteMapBase(iRegPNoSp dst, immByteMapBase con)
%{
  match(Set dst con);

  ins_cost(INSN_COST);
  format %{ "adr  $dst, $con\t# Byte Map Base" %}

  ins_encode(aarch64_enc_mov_byte_map_base(dst, con));

  ins_pipe(ialu_imm);
%}

A typical backtrace as below:
#0  0x0000ffff280866e0 in raise () from /lib64/libc.so.6
#1  0x0000ffff28087a8c in abort () from /lib64/libc.so.6
#2  0x0000ffff27af1e7c in os::abort (dump_core=true, siginfo=<optimized out>, context=<optimized out>)
    at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/os/linux/vm/os_linux.cpp:1601
#3  0x0000ffff27cd48a0 in VMError::report_and_die (this=this at entry=0xffff270f6c58) at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/share/vm/utilities/vmError.cpp:1159
#4  0x0000ffff27aff4dc in JVM_handle_linux_signal (sig=11, info=info at entry=0xffff270f6d80, ucVoid=0xffff270f6e00, abort_if_unrecognized=abort_if_unrecognized at entry=1)
    at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/os_cpu/linux_aarch64/vm/os_linux_aarch64.cpp:410
#5  0x0000ffff27aee3b0 in signalHandler (sig=<optimized out>, info=0xffff270f6d80, uc=<optimized out>)
    at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/os/linux/vm/os_linux.cpp:4839
#6  <signal handler called>
#7  Klass::layout_helper (this=0x8b56b87d8) at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/share/vm/oops/klass.hpp:577
#8  Klass::oop_is_instance (this=0x8b56b87d8) at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/share/vm/oops/klass.hpp:577
#9  oopDesc::is_instance (this=<optimized out>) at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/share/vm/oops/oop.inline.hpp:186
#10 ServiceUtil::visible_oop (o=<optimized out>) at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/share/vm/services/serviceUtil.hpp:47
#11 JavaThreadBlockedOnMonitorEnterState::JavaThreadBlockedOnMonitorEnterState (obj_m=0xffff180114d8, java_thread=0xffff20029000, this=<synthetic pointer>)
    at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/share/vm/services/threadService.hpp:552
#12 ObjectMonitor::enter (this=0xffff180114d8, __the_thread__=__the_thread__ at entry=0xffff20029000)
    at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/share/vm/runtime/objectMonitor.cpp:390
#13 0x0000ffff27c271dc in ObjectSynchronizer::slow_enter (__the_thread__=0xffff20029000, lock=0xffff270f8130, obj=...)
    at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/share/vm/runtime/handles.hpp:79
#14 ObjectSynchronizer::fast_enter (obj=..., lock=lock at entry=0xffff270f8130, attempt_rebias=attempt_rebias at entry=true, __the_thread__=__the_thread__ at entry=0xffff20029000)
    at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/share/vm/runtime/synchronizer.cpp:183
#15 0x0000ffff27bb10b0 in SharedRuntime::complete_monitor_locking_C (_obj=0xffff21730000, lock=0xffff270f8130, thread=0xffff20029000)
    at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/share/vm/runtime/sharedRuntime.cpp:1879
#16 0x0000ffff256ffff8 in ?? ()
#17 0x0000ffff277a0414 in InstanceKlass::register_finalizer (i=<optimized out>, __the_thread__=0xfffeebc09d20)
    at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/share/vm/utilities/growableArray.hpp:261
#18 0x0000ffff277a0414 in InstanceKlass::register_finalizer (i=<optimized out>, __the_thread__=0xffff20029000)
    at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/share/vm/utilities/growableArray.hpp:261
#19 0x0000ffff270f8390 in ?? ()
#20 0x00000007ad622658 in ?? ()

Passing byte_map_base instead of oop to complete_monitor_locking_C:
#15 0x0000ffff27bb10b0 in SharedRuntime::complete_monitor_locking_C (_obj=0xffff21730000, lock=0xffff270f8130, thread=0xffff20029000)
    at /usr1/jenkins/BiShengJDK/open_source/openjdk/openjdk/hotspot/src/share/vm/runtime/sharedRuntime.cpp:1879

Called by complete_monitor_lock_Java:
   0xffff256fffc0:   nop
   0xffff256fffc4:   sub  sp, sp, #0x10
   0xffff256fffc8:   stp   x29, x30, [sp]
   0xffff256fffcc:    mov x9, sp
   0xffff256fffd0:   str   x9, [x28, #592]
   0xffff256fffd4:   mov x0, x1
   0xffff256fffd8:   mov x1, x2
   0xffff256fffdc:   mov x2, x28
   0xffff256fffe0:   adr  x9, 0xffff256ffff8
   0xffff256fffe4:   mov x8, #0xff0                      // #4080
   0xffff256fffe8:   movk      x8, #0x27bb, lsl #16
   0xffff256fffec:   movk      x8, #0xffff, lsl #32
   0xffff256ffff0:    stp   xzr, x9, [sp, #-16]!
   0xffff256ffff4:    blr   x8
   0xffff256ffff8:    add sp, sp, #0x10
   0xffff256ffffc:    str   xzr, [x28, #592]
   0xffff25700000: str   xzr, [x28, #600]
   0xffff25700004: ldr   x10, [x28, #8]
   0xffff25700008: cbnz       x10, 0xffff25700018
   0xffff2570000c: ldp  x29, x30, [sp]
   0xffff25700010: add sp, sp, #0x10
   0xffff25700014: ret
   0xffff25700018: adrp       x10, 0xffff25580000
   0xffff2570001c: add x10, x10, #0x340
   0xffff25700020: mov x12, xzr
   0xffff25700024: ldp  x29, x30, [sp]
   0xffff25700028: add sp, sp, #0x10
   0xffff2570002c: br    x10

Called by J 1821 C2 java.lang.ref.Finalizer.register(Ljava/lang/Object;)V (10 bytes) @ 0x0000ffff25caf0bc [0x0000ffff25caee80+0x23c]
   0xffff25caf0b4:  adrp       x1, 0xffff21730000
   0xffff25caf0b8:  bl    0xffff256fffc0

Which encoding byte_map_base instead of the specific oop and passing it to complete_monitor_lock_Java.

So the entire story is that the predicate rule of immByteMapBase caused an incorrect ConP match when a corresponding JNIHandleBlock with an oop was allocated to the address of byte_map_base by malloc.
I have a simple fix that just remove the specialized match rule for immByteMapBase below, and crashes disappeared.
diff --git a/hotspot/src/cpu/aarch64/vm/aarch64.ad b/hotspot/src/cpu/aarch64/vm/aarch64.ad
index d73d5d457..5305467c1 100644
--- a/hotspot/src/cpu/aarch64/vm/aarch64.ad
+++ b/hotspot/src/cpu/aarch64/vm/aarch64.ad
@@ -3054,11 +3054,6 @@ encode %{
     assert(off == 0, "assumed offset == 0");
   %}

-  enc_class aarch64_enc_mov_byte_map_base(iRegP dst, immByteMapBase src) %{
-    MacroAssembler _masm(&cbuf);
-    __ load_byte_map_base($dst$$Register);
-  %}
-
   enc_class aarch64_enc_mov_n(iRegN dst, immN src) %{
     MacroAssembler _masm(&cbuf);
     Register dst_reg = as_Register($dst$$reg);
@@ -4311,19 +4306,6 @@ operand immPollPage()
   interface(CONST_INTER);
%}

-// Card Table Byte Map Base
-operand immByteMapBase()
-%{
-  // Get base of card map
-  predicate((jbyte*)n->get_ptr() ==
-       ((CardTableModRefBS*)(Universe::heap()->barrier_set()))->byte_map_base);
-  match(ConP);
-
-  op_cost(0);
-  format %{ %}
-  interface(CONST_INTER);
-%}
-
// Pointer Immediate Minus One
// this is used when we want to write the current PC to the thread anchor
operand immP_M1()
@@ -6772,20 +6754,6 @@ instruct loadConPollPage(iRegPNoSp dst, immPollPage con)
   ins_pipe(ialu_imm);
%}

-// Load Byte Map Base Constant
-
-instruct loadByteMapBase(iRegPNoSp dst, immByteMapBase con)
-%{
-  match(Set dst con);
-
-  ins_cost(INSN_COST);
-  format %{ "adr  $dst, $con\t# Byte Map Base" %}
-
-  ins_encode(aarch64_enc_mov_byte_map_base(dst, con));
-
-  ins_pipe(ialu_imm);
-%}
-
// Load Narrow Pointer Constant

instruct loadConN(iRegNNoSp dst, immN con)

Does anyone have any suggestions on this?

Yadong
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/aarch64-port-dev/attachments/20250706/7760a466/attachment-0001.htm>


More information about the aarch64-port-dev mailing list