aarch64: MacroAssembler::arrays_equals reads out of bounds

XMLWordPrintable


      We encountered this while running with CompactObjectHeaders.
      ```
      #
      # A fatal error has been detected by the Java Runtime Environment:
      #
      # SIGSEGV (0xb) at pc=0x0000ffff9bea236c, pid=433432, tid=433450
      #
      # JRE version: Java(TM) SE Runtime Environment (26.0) (fastdebug build 26-internal)
      ```

      We crash inside on instruction address 0x0000ffff9bea236c:
      ```
      ;; array_equalsL{
        0x0000ffff9bea2330: 3f00 02eb | 4006 0054
       ;; 0x0
        0x0000ffff9bea2338: 0000 80d2 | 2106 00b4 | 2a08 40b9 | e205 00b4 | 4a11 0011 | 5f21 00f1 | 6d04 0054 | 238c 40f8
        0x0000ffff9bea2358: 5f01 03f1 | 2a03 0054 | 448c 40f8 | e50f 0acb | 288c 40f8 | 498c 40f8 | 4a41 00f1 | 4d01 0054
        0x0000ffff9bea2378: 6400 04ca | 2404 00b5 | 238c 40f8 | 448c 40f8 | 5f21 00f1 | 4d01 0054 | 1f01 09eb | a0fe ff54
        0x0000ffff9bea2398: 1a00 0014 | 6400 04ca | 0901 09ca | 2921 c59a | 8500 09aa | bf00 1feb | 1100 0014 | 0901 09ca
        0x0000ffff9bea23b8: 4902 00b5 | 0b00 0014 | 448c 40f8 | 6500 04ca | c501 00b5

        0x0000ffff9bea23cc: ; {runtime_call Stub::Stub Generator large_array_equals_stub}
        0x0000ffff9bea23cc: 6d0f 0094 | 0c00 0014 | e003 02aa | 0a00 0014 | e50f 0acb | 2304 40f9 | 4404 40f9 | 6400 04ca
        0x0000ffff9bea23ec: 8520 c59a | bf00 1feb | e017 9f9a | 0200 0014
       ;; 0x1
        0x0000ffff9bea23fc: ;*invokestatic equals {reexecute=0 rethrow=0 return_oop=0}
                            ; - java.lang.invoke.LambdaFormEditor$Transform::equals@20 (line 111)
                            ; - java.lang.invoke.LambdaFormEditor::getInCache@105 (line 399)
                            ; - java.lang.invoke.LambdaFormEditor::filterArgumentForm@12 (line 704)
                            ; - java.lang.invoke.MethodHandleImpl::makePairwiseConvertByEditor@387 (line 340)
        0x0000ffff9bea23fc: e003 40b2
       ;; } array_equals
      ```

      ```
      0x0000ffff9bea233c: 21 06 00 B4 cbz x1, #0xffff9bea2400
      0x0000ffff9bea2340: 2A 08 40 B9 ldr w10, [x1, #8]
      0x0000ffff9bea2344: E2 05 00 B4 cbz x2, #0xffff9bea2400
      0x0000ffff9bea2348: 4A 11 00 11 add w10, w10, #4
      0x0000ffff9bea234c: 5F 21 00 F1 cmp x10, #8
      0x0000ffff9bea2350: 6D 04 00 54 b.le #0xffff9bea23dc
      0x0000ffff9bea2354: 23 8C 40 F8 ldr x3, [x1, #8]!
      0x0000ffff9bea2358: 5F 01 03 F1 cmp x10, #0xc0
      0x0000ffff9bea235c: 2A 03 00 54 b.ge #0xffff9bea23c0
      0x0000ffff9bea2360: 44 8C 40 F8 ldr x4, [x2, #8]!
      0x0000ffff9bea2364: E5 0F 0A CB neg x5, x10, lsl #3
      0x0000ffff9bea2368: 28 8C 40 F8 ldr x8, [x1, #8]!
      0x0000ffff9bea236c: 49 8C 40 F8 ldr x9, [x2, #8]! <-- Crash
      0x0000ffff9bea2370: 4A 41 00 F1 subs x10, x10, #0x10
      0x0000ffff9bea2374: 4D 01 00 54 b.le #0xffff9bea239c
      0x0000ffff9bea2378: 64 00 04 CA eor x4, x3, x4
      0x0000ffff9bea237c: 24 04 00 B5 cbnz x4, #0xffff9bea2400
      0x0000ffff9bea2380: 23 8C 40 F8 ldr x3, [x1, #8]!
      0x0000ffff9bea2384: 44 8C 40 F8 ldr x4, [x2, #8]!
      0x0000ffff9bea2388: 5F 21 00 F1 cmp x10, #8
      0x0000ffff9bea238c: 4D 01 00 54 b.le #0xffff9bea23b4
      0x0000ffff9bea2390: 1F 01 09 EB cmp x8, x9
      0x0000ffff9bea2394: A0 FE FF 54 b.eq #0xffff9bea2368
      0x0000ffff9bea2398: 1A 00 00 14 b #0xffff9bea2400
      0x0000ffff9bea239c: 64 00 04 CA eor x4, x3, x4
      ```

      The address we crash on a load of an address right after the last object in eden. (Register `R2`).

      ```
      siginfo: si_signo: 11 (SIGSEGV), si_code: 2 (SEGV_ACCERR), si_addr: 0x00000000d0530000

      Heap:
       DefNew total 4800K, used 4448K [0x00000000d0000000, 0x00000000d0530000, 0x00000000e0000000)
        eden space 4288K, 100% used [0x00000000d0100000, 0x00000000d0530000, 0x00000000d0530000)
        from space 512K, 31% used [0x00000000d0000000, 0x00000000d0028090, 0x00000000d0080000)
        to space 512K, 0% used [0x00000000d0080000, 0x00000000d0080000, 0x00000000d0100000)
       Tenured total 10496K, used 10457K [0x00000000e0000000, 0x00000000e0a40000, 0x0000000100000000)
        the space 10496K, 99% used [0x00000000e0000000, 0x00000000e0a366b8, 0x00000000e0a40000)

      R1 =0x00000000d0025260 is pointing into object: [B
      {0x00000000d0025250} - klass: {type array byte} - flags: is_cloneable_fast

       - length: 7
       - 0: 9
       - 1: 0
       - 2: e
       - 3: f
       - 4: 16
       - 5: 24 $
       - 6: 2c ,
      R2 =0x00000000d052fff8 is pointing into object: [B
      {0x00000000d052fff0} - klass: {type array byte} - flags: is_cloneable_fast

       - length: 3
       - 0: 5
       - 1: 3d =
       - 2: 0

       ```

      The problem seems to be that the code is written with the expectation that reading 16 bytes from the `align_down(length_offset, BytesPerWord);` is is correct. With CompactObjectHeaders we have a header of `8 bytes` the length offset is `8 bytes` and the payload for a byte array has an offset of `12 bytes`. A byte array with a length of at most `4` will be only `16 bytes`. The first read in the emitted code is at offset `8 bytes` and the second is at `16 bytes` which is out of bounds.

      ``` c++
          [...]
          ldr(tmp4, Address(pre(a2, start_offset))); // Load from offset 8 [4 byte length + 3 bytes payload + 1 byte padding]
          sub(tmp5, zr, cnt1, LSL, 3 + log_elem_size);

          // Main 16 byte comparison loop with 2 exits
          bind(NEXT_DWORD); {
            ldr(tmp1, Address(pre(a1, wordSize)));
            ldr(tmp2, Address(pre(a2, wordSize))); // Load from offset 16 OOB for Byte arrays with COH and length <= 4
            subs(cnt1, cnt1, 2 * elem_per_word);
            br(LE, TAIL);
            eor(tmp4, tmp3, tmp4);
            cbnz(tmp4, DONE);
            ldr(tmp3, Address(pre(a1, wordSize)));
            ldr(tmp4, Address(pre(a2, wordSize)));
            cmp(cnt1, (u1)elem_per_word);
            br(LE, TAIL2);
            cmp(tmp1, tmp2);
          } br(EQ, NEXT_DWORD);
          b(DONE);
          [...]
      ```

      This seems to be some combination of assumptions from before CompactObjectHeader and the patch to relax the payload alignment to not be 8 bytes. Because at that time TypeArrays were always larger than 24 bytes with CompressedKlassPointers and at least 32 bytes without CompressedKlassPointer.

      This issue has really only been visible because we got allocated an object at the end of eden, which probably got a lot more likely because of resent TLAB changes to remove the prefetch extra buffer we added beyond end. However it looks to me that this code can also end up with a false negative, that is two small arrays which are equal, end up looking like they are different because it reads and compares 8 bytes beyond the object payload.

            Assignee:
            Roman Kennke
            Reporter:
            Axel Boldt-Christmas
            Votes:
            0 Vote for this issue
            Watchers:
            7 Start watching this issue

              Created:
              Updated: