JDK-8336729 : C2: Div/Mod nodes without zero check could be split through iv phi of outer loop of long counted loop nest resulting in SIGFPE
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 17,21,22,23,24
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2024-07-18
  • Updated: 2024-10-24
  • Resolved: 2024-08-20
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 24
24 b12Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Credit in the PR would be appreciated. I spend quite some time to extract a nice JASM from nasty class files.
Also: give Christian credit for his analysis.

Affected: JDK23, 22, 21, 17 (did not reproduce with JDK11, but I did not check very deeply here)
I only can reproduce it on debug, since I need StressGCM.

java -jar ~/Documents/asmtools-7.0-build/release/lib/asmtools.jar jasm X.jasm

/oracle-work/jdk-fork4/build/linux-x64-debug/jdk/bin/java -XX:+StressGCM X
implicit exception happened at 0x00007f7574bc987e
[CodeBlob (0x00007f7574bc9688)]
Framesize: 4
   0 ldc2_w 10
   3 lstore_0
   4 lload_0
   5 lconst_1
   6 lcmp
   7 ifle 55
  0    bci: 7    BranchData         taken(20569) displacement(168)
                                    not taken(102845)
  10 ldc2_w 10
  13 lload_0
  14 lrem
  15 putstatic 4 <X.lFld:J> 
  18 iconst_0
  19 istore_2
  20 iload_2
  21 bipush 10
  23 if_icmpge 46
  32   bci: 23   BranchData         taken(102845) displacement(112)
                                    not taken(1028450)
  26 getstatic 5 <X.flag:Z> 
  29 ifne 36
  64   bci: 29   BranchData         taken(514225) displacement(56)
                                    not taken(514225)
  32 iconst_1
  33 goto 37
  96   bci: 33   JumpData           taken(514225) displacement(24)
  36 iconst_0
  37 putstatic 5 <X.flag:Z> 
  40 iinc #2 1
  43 goto 20
  120  bci: 43   JumpData           taken(1028450) displacement(-88)
  46 lload_0
  47 ldc2_w 2
  50 lsub
  51 lstore_0
  52 goto 4
  144  bci: 52   JumpData           taken(102845) displacement(-144)
  55 return

Compiled method (c2) 508  463       4       X::test (56 bytes)
 total in heap  [0x00007f7574bc9688,0x00007f7574bc99a8] = 800
 relocation     [0x00007f7574bc9770,0x00007f7574bc9788] = 24
 main code      [0x00007f7574bc97a0,0x00007f7574bc9980] = 480
 stub code      [0x00007f7574bc9980,0x00007f7574bc9998] = 24
 oops           [0x00007f7574bc9998,0x00007f7574bc99a0] = 8
 metadata       [0x00007f7574bc99a0,0x00007f7574bc99a8] = 8
 immutable data [0x00007f751019ef50,0x00007f751019f0a8] = 344
 dependencies   [0x00007f751019ef50,0x00007f751019ef58] = 8
 scopes pcs     [0x00007f751019ef58,0x00007f751019f068] = 272
 scopes data    [0x00007f751019f068,0x00007f751019f0a8] = 64

[Disassembly]
--------------------------------------------------------------------------------
[Constant Pool (empty)]

--------------------------------------------------------------------------------

[Verified Entry Point]
  # {method} {0x00007f7544707b88} 'test' '()V' in 'X'
  #           [sp+0x20]  (sp of caller)
 ;; N1: #	out( B1 ) <- in( B11 )  Freq: 1
 ;; B1: #	out( B5 ) <- BLOCK HEAD IS JUNK  Freq: 1
  0x00007f7574bc97a0:   mov    %eax,-0x18000(%rsp)
  0x00007f7574bc97a7:   push   %rbp
  0x00007f7574bc97a8:   sub    $0x10,%rsp
  0x00007f7574bc97ac:   cmpl   $0x1,0x20(%r15)
  0x00007f7574bc97b4:   jne    0x00007f7574bc9972           ;*synchronization entry
                                                            ; - X::test@-1
  0x00007f7574bc97ba:   movabs $0x6666666666666667,%rax
  0x00007f7574bc97c4:   movabs $0x6355f0368,%rcx            ;   {oop(a 'java/lang/Class'{0x00000006355f0368} = 'X')}
  0x00007f7574bc97ce:   mov    $0xa,%r10d
  0x00007f7574bc97d4:   imul   %r10
  0x00007f7574bc97d7:   lea    (%rdx,%rdx,1),%r10
  0x00007f7574bc97db:   sar    %rdx
  0x00007f7574bc97de:   and    $0xfffffffffffffff8,%r10
  0x00007f7574bc97e2:   and    $0xfffffffffffffffe,%rdx
  0x00007f7574bc97e6:   add    %r10,%rdx
  0x00007f7574bc97e9:   mov    $0xa,%r10d
  0x00007f7574bc97ef:   sub    %rdx,%r10
  0x00007f7574bc97f2:   mov    %r10,0x70(%rcx)              ;*putstatic lFld {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - X::test@15
  0x00007f7574bc97f6:   mov    $0x6,%r8d
  0x00007f7574bc97fc:   mov    $0x8,%edi
  0x00007f7574bc9801:   mov    $0x2,%edx
  0x00007f7574bc9806:   jmpq   0x00007f7574bc9892
 ;; B2: #	out( B3 ) <- in( B8 ) top-of-loop Freq: 2.50009
  0x00007f7574bc980b:   sub    %r10d,%ebx
  0x00007f7574bc980e:   lea    -0x1(%rbx),%r11d
  0x00007f7574bc9812:   movslq %r10d,%rbp
  0x00007f7574bc9815:   shr    $0x1f,%r11d
  0x00007f7574bc9819:   lea    -0x1(%r11,%rbx,1),%r10d
  0x00007f7574bc981e:   sar    %r10d
  0x00007f7574bc9821:   neg    %r10d
  0x00007f7574bc9824:   shl    %r10d
  0x00007f7574bc9827:   movslq %r10d,%r10
  0x00007f7574bc982a:   sub    %r10,%rbp
  0x00007f7574bc982d:   add    %rdi,%rbp                    ;*lsub {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - X::test@50
  0x00007f7574bc9830:   lea    0x2(%rbp),%r10
  0x00007f7574bc9834:   mov    $0xa,%eax
  0x00007f7574bc9839:   movabs $0x8000000000000000,%rdx
  0x00007f7574bc9843:   cmp    %rdx,%rax
  0x00007f7574bc9846:   jne    0x00007f7574bc9850
  0x00007f7574bc9848:   xor    %edx,%edx
  0x00007f7574bc984a:   cmp    $0xffffffffffffffff,%r10
  0x00007f7574bc984e:   je     0x00007f7574bc9855
  0x00007f7574bc9850:   cqto   
  0x00007f7574bc9852:   idiv   %r10
  0x00007f7574bc9855:   mov    %rdx,0x70(%rcx)              ;*putstatic lFld {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - X::test@15
  0x00007f7574bc9859:   nop
  0x00007f7574bc985a:   nop
  0x00007f7574bc985b:   nop
  0x00007f7574bc985c:   nop
  0x00007f7574bc985d:   nop
  0x00007f7574bc985e:   nop
  0x00007f7574bc985f:   nop                                 ;*ifle {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - X::test@7
 ;; B3: #	out( B11 B4 ) <- in( B10 B2 B9 ) top-of-loop Freq: 6.00018
  0x00007f7574bc9860:   mov    $0xa,%eax
  0x00007f7574bc9865:   movabs $0x8000000000000000,%rdx
  0x00007f7574bc986f:   cmp    %rdx,%rax
  0x00007f7574bc9872:   jne    0x00007f7574bc987c
  0x00007f7574bc9874:   xor    %edx,%edx
  0x00007f7574bc9876:   cmp    $0xffffffffffffffff,%rbp
  0x00007f7574bc987a:   je     0x00007f7574bc9881
  0x00007f7574bc987c:   cqto   
  0x00007f7574bc987e:   idiv   %rbp
  0x00007f7574bc9881:   cmp    $0x1,%rbp
  0x00007f7574bc9885:   jle    0x00007f7574bc993a
 ;; B4: #	out( B5 ) <- in( B3 )  Freq: 5.00018
  0x00007f7574bc988b:   lea    -0x2(%rbp),%r8
  0x00007f7574bc988f:   mov    %rbp,%rdi                    ;*lrem {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - X::test@14
 ;; B5: #	out( B10 B6 ) <- in( B1 B4 ) Loop( B5-B4 inner ) Freq: 6.00018
  0x00007f7574bc9892:   mov    %rdx,0x70(%rcx)              ;*goto {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - X::test@52
  0x00007f7574bc9896:   mov    %edi,%r11d
  0x00007f7574bc9899:   lea    -0x4(%rdi),%rsi
  0x00007f7574bc989d:   movslq %r11d,%r10                   ;*goto {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - X::test@43
  0x00007f7574bc98a0:   mov    0x490(%r15),%r9              ; ImmutableOopMap {rcx=Oop }
                                                            ;*goto {reexecute=1 rethrow=0 return_oop=0}
                                                            ; - (reexecute) X::test@52
  0x00007f7574bc98a7:   test   %eax,(%r9)                   ;*goto {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - X::test@52
                                                            ;   {poll}
  0x00007f7574bc98aa:   mov    $0x1,%ebx
  0x00007f7574bc98af:   sub    %r11d,%ebx
  0x00007f7574bc98b2:   mov    $0x3,%r11d
  0x00007f7574bc98b8:   sub    %r10,%r11
  0x00007f7574bc98bb:   mov    $0xfffffff8,%r10d
  0x00007f7574bc98c1:   cmp    $0xfffffffe,%ebx
  0x00007f7574bc98c4:   jge    0x00007f7574bc9932           ;*ifle {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - X::test@7
 ;; B6: #	out( B12 B7 ) <- in( B5 )  Freq: 5.00018
  0x00007f7574bc98c6:   lea    -0x6(%rdi),%r8
  0x00007f7574bc98ca:   mov    $0xa,%eax
  0x00007f7574bc98cf:   movabs $0x8000000000000000,%rdx
  0x00007f7574bc98d9:   cmp    %rdx,%rax
  0x00007f7574bc98dc:   jne    0x00007f7574bc98e6
  0x00007f7574bc98de:   xor    %edx,%edx
  0x00007f7574bc98e0:   cmp    $0xffffffffffffffff,%r8
  0x00007f7574bc98e4:   je     0x00007f7574bc98eb
  0x00007f7574bc98e6:   cqto   
  0x00007f7574bc98e8:   idiv   %r8
  0x00007f7574bc98eb:   mov    %rdx,%r8
  0x00007f7574bc98ee:   lea    -0x8(%rdi),%rbp
  0x00007f7574bc98f2:   mov    %r11d,%r9d
  0x00007f7574bc98f5:   lea    -0x2(%rdi),%r11
  0x00007f7574bc98f9:   mov    $0xa,%eax
  0x00007f7574bc98fe:   movabs $0x8000000000000000,%rdx
  0x00007f7574bc9908:   cmp    %rdx,%rax
  0x00007f7574bc990b:   jne    0x00007f7574bc9915
  0x00007f7574bc990d:   xor    %edx,%edx
  0x00007f7574bc990f:   cmp    $0xffffffffffffffff,%r11
  0x00007f7574bc9913:   je     0x00007f7574bc991a
  0x00007f7574bc9915:   cqto   
  0x00007f7574bc9917:   idiv   %r11
  0x00007f7574bc991a:   cmp    $0xfffffffc,%r9d
  0x00007f7574bc991e:   jge    0x00007f7574bc994d
 ;; B7: #	out( B8 ) <- in( B6 )  Freq: 5.00017
  0x00007f7574bc9920:   mov    %r8,0x70(%rcx)               ;*putstatic lFld {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - X::test@15
 ;; B8: #	out( B2 B9 ) <- in( B12 B7 )  Freq: 5.00018
  0x00007f7574bc9924:   cmp    %ebx,%r10d
  0x00007f7574bc9927:   jg     0x00007f7574bc980b
 ;; B9: #	out( B3 ) <- in( B8 )  Freq: 2.50009
  0x00007f7574bc992d:   jmpq   0x00007f7574bc9860
 ;; B10: #	out( B3 ) <- in( B5 )  Freq: 1
  0x00007f7574bc9932:   mov    %r8,%rbp
  0x00007f7574bc9935:   jmpq   0x00007f7574bc9860           ;*ifle {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - X::test@7
 ;; B11: #	out( N1 ) <- in( B3 )  Freq: 1
  0x00007f7574bc993a:   add    $0x10,%rsp
  0x00007f7574bc993e:   pop    %rbp
  0x00007f7574bc993f:   cmp    0x488(%r15),%rsp             ;   {poll_return}
  0x00007f7574bc9946:   ja     0x00007f7574bc995c
  0x00007f7574bc994c:   retq   
 ;; B12: #	out( B8 ) <- in( B6 )  Freq: 5.06657e-06
  0x00007f7574bc994d:   mov    %rdx,0x70(%rcx)              ;*putstatic lFld {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - X::test@15
  0x00007f7574bc9951:   mov    $0xfffffffc,%r10d
  0x00007f7574bc9957:   mov    %rsi,%rbp
  0x00007f7574bc995a:   jmp    0x00007f7574bc9924           ;*ifle {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - X::test@7
  0x00007f7574bc995c:   movabs $0x7f7574bc993f,%r10         ;   {internal_word}
  0x00007f7574bc9966:   mov    %r10,0x4a0(%r15)
  0x00007f7574bc996d:   jmpq   0x00007f757457e600           ;   {runtime_call SafepointBlob}
  0x00007f7574bc9972:   callq  Stub::nmethod_entry_barrier  ;   {runtime_call StubRoutines (final stubs)}
  0x00007f7574bc9977:   jmpq   0x00007f7574bc97ba
  0x00007f7574bc997c:   hlt    
  0x00007f7574bc997d:   hlt    
  0x00007f7574bc997e:   hlt    
  0x00007f7574bc997f:   hlt    
[Exception Handler]
  0x00007f7574bc9980:   jmpq   0x00007f757462e160           ;   {no_reloc}
[Deopt Handler Code]
  0x00007f7574bc9985:   callq  0x00007f7574bc998a
  0x00007f7574bc998a:   subq   $0x5,(%rsp)
  0x00007f7574bc998f:   jmpq   0x00007f757457fa20           ;   {runtime_call DeoptimizationBlob}
  0x00007f7574bc9994:   hlt    
  0x00007f7574bc9995:   hlt    
  0x00007f7574bc9996:   hlt    
  0x00007f7574bc9997:   hlt    
--------------------------------------------------------------------------------
[/Disassembly]
pc-bytecode offsets:
PcDesc(pc=0x00007f7574bc979f offset=ffffffff bits=0):
PcDesc(pc=0x00007f7574bc97ba offset=1a bits=0):
   X::test@-1
PcDesc(pc=0x00007f7574bc97f6 offset=56 bits=0):
   X::test@15
PcDesc(pc=0x00007f7574bc9830 offset=90 bits=0):
   X::test@50
PcDesc(pc=0x00007f7574bc9859 offset=b9 bits=0):
   X::test@15
PcDesc(pc=0x00007f7574bc9860 offset=c0 bits=0):
   X::test@7
PcDesc(pc=0x00007f7574bc9892 offset=f2 bits=0):
   X::test@14
PcDesc(pc=0x00007f7574bc9896 offset=f6 bits=0):
   X::test@52
PcDesc(pc=0x00007f7574bc98a0 offset=100 bits=0):
   X::test@43
PcDesc(pc=0x00007f7574bc98a7 offset=107 bits=1):
   X::test@52  reexecute=true
   Locals
    - l0: 0
    - l1: reg r8 [16],long
    - l2: empty
PcDesc(pc=0x00007f7574bc98aa offset=10a bits=0):
   X::test@52
PcDesc(pc=0x00007f7574bc98c6 offset=126 bits=0):
   X::test@7
PcDesc(pc=0x00007f7574bc9924 offset=184 bits=0):
   X::test@15
PcDesc(pc=0x00007f7574bc993a offset=19a bits=0):
   X::test@7
PcDesc(pc=0x00007f7574bc9951 offset=1b1 bits=0):
   X::test@15
PcDesc(pc=0x00007f7574bc995c offset=1bc bits=0):
   X::test@7
PcDesc(pc=0x00007f7574bc9999 offset=1f9 bits=0):
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGFPE (0x8) at pc=0x00007f7574bc987e, pid=309595, tid=309596
#
# JRE version: Java(TM) SE Runtime Environment (23.0) (fastdebug build 23-internal-2024-05-22-1054280.emanuel...)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (fastdebug 23-internal-2024-05-22-1054280.emanuel..., mixed mode, tiered, compressed oops, compressed class ptrs, g1 gc, linux-amd64)
# Problematic frame:
# J 463 c2 X.test()V (56 bytes) @ 0x00007f7574bc987e [0x00007f7574bc97a0+0x00000000000000de]
#
# Core dump will be written. Default location: Core dumps may be processed with "/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E" (or dumping to /oracle-work/triage/current/core.309595)
#
# An error report file with more information is saved as:
# /oracle-work/triage/current/hs_err_pid309595.log
#
# If you would like to submit a bug report, please visit:
#   https://bugreport.java.com/bugreport/crash.jsp
#
Aborted (core dumped)

Stack: [0x00007f7582c84000,0x00007f7582d85000],  sp=0x00007f7582d838f0,  free space=1022k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
J 463 c2 X.test()V (56 bytes) @ 0x00007f7574bc987e [0x00007f7574bc97a0+0x00000000000000de]
j  X.main([Ljava/lang/String;)V+9
v  ~StubRoutines::call_stub 0x00007f757445fd01
V  [libjvm.so+0xe69379]  JavaCalls::call_helper(JavaValue*, methodHandle const&, JavaCallArguments*, JavaThread*)+0x4a9  (javaCalls.cpp:415)
V  [libjvm.so+0xfa1070]  jni_invoke_static(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, JavaThread*) [clone .constprop.1]+0x360  (jni.cpp:888)
V  [libjvm.so+0xfa4773]  jni_CallStaticVoidMethod+0x193  (jni.cpp:1717)
C  [libjli.so+0x3a00]  invokeStaticMainWithArgs+0x70  (java.c:418)
C  [libjli.so+0x49dd]  JavaMain+0xd9d  (java.c:623)
C  [libjli.so+0x7cb9]  ThreadJavaMain+0x9  (java_md.c:653)

Comments
Changeset: 55a97ec8 Branch: master Author: Christian Hagedorn <chagedorn@openjdk.org> Date: 2024-08-20 15:47:16 +0000 URL: https://git.openjdk.org/jdk/commit/55a97ec8793242c0cacbafd3a4fead25cdce2934
20-08-2024

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/20594 Date: 2024-08-15 11:11:54 +0000
15-08-2024

Note: 11u does not seem to be affected since we can only trigger this bug with LongCountedLoops which have only been added in JDK 16.
15-08-2024

ILW = Same as JDK-8299259 = P3
18-07-2024

[~chagedorn] Investigated this bug already a bit, here his comments: Seems unrelated. JDK-8331717 is related to Loop Predication while in X.jasm, we do not apply any Loop Predication. (to verify: X.jasm reproduces with -XX:-UseLoopPredicate, while the test of 8331717 does not). I had a look and the problem seems the following: We prevent split_thru_phi for div and mod nodes through counted loop phis when the backedge input type includes zero. This is necessary since we update a counted loop iv phi in such a way that it could not include zero while the backedge input could be zero. The current code, however, only applies this bailout if we have an actual BaseCountedLoop where the iv phi type is improved (https://github.com/openjdk/jdk/blob/ba67ad63ae7d7d399e41ab258576123fb6d9502c/src/hotspot/share/opto/loopopts.cpp#L302-L304): bool PhaseIdealLoop::is_divisor_counted_loop_phi(const Node* divisor, const Node* loop) { return loop->is_BaseCountedLoop() && divisor->is_Phi() && divisor->in(0) == loop; } In the test case, we have a LongCountedLoop, for which we set the loop iv phi to a non-zero type while the backedge could include zero. We have a Mod node with the iv phi as input. Split thru phi is prevented due to the backedge including zero. So far, so good. But when expanding the LongCountedLoop to a Loop -> CountedLoop nest, we set the iv phi of both loops to the non-zero LongCountedLoop iv phi type from before. When later trying to split the Mod node again through the outer Loop iv phi, is_divisor_counted_loop_phi() returns false because it is not a BaseCountedLoop. We miss here that for long counted loop, we could have such an improved counted loop phi type while the node is actually a Loop. As a result, we wrongly allow split thru phi and later crash at runtime when executing a division by zero on the backedge in the very last iteration. The result cannot be observed, though. Recommendation: This can be treated equally to the originally done open fixes for preventing splitting a div/mod through a loop phi (see JDK-8299259 and related issues) in the first place. The fix for this bug would be an extension to that. Possible Fix: The easiest fix would be to update is_divisor_counted_loop_phi() such that we disallow the split thru phi for any kind of loop phis (regardless of whether it's a counted loop or not). Someone else asked: Is this a divide by zero that would have happened anyway? No, it would not. The loop looks like this: long i = 10; i > 0; i -= 2. So, in the last iteration, we have i = 2 and then we decrement it to zero. We should not re-enter the loop (i.e. take the loop exit and not the backedge to re-iterate again). The problem, however, is that x / i from the backedge floats above the loop exit check. We execute x / i with i = 0 and crash. We should not observe this in Java. In this case it's a division by zero whose result cannot be observed (we will not take the backedge from which the division by zero was floaoting from). It's the same as https://bugs.openjdk.org/browse/JDK-8299259 but actually another case not covered.
18-07-2024