https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81954
Bug ID: 81954 Summary: gcc8 too aggressively reorders memory access beyond condition Product: gcc Version: 8.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c Assignee: unassigned at gcc dot gnu.org Reporter: naruse at airemix dot jp Target Milestone: --- Created attachment 42032 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=42032&action=edit vm.i, generated from vm.c Environments: shown below the preprocessed file (*.i*): attached vm.i How to reproduce from source: % gcc8 -v Using built-in specs. COLLECT_GCC=gcc8 COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc8/gcc/x86_64-portbld-freebsd10.3/8.0.0/lto-wrapper Target: x86_64-portbld-freebsd10.3 Configured with: /wrkdirs/usr/ports/lang/gcc8-devel/work/gcc-8-20170820/configure --with-build-config=bootstrap-debug --disable-nls --enable-gnu-indirect-function --libdir=/usr/local/lib/gcc8 --libexecdir=/usr/local/libexec/gcc8 --program-suffix=8 --with-as=/usr/local/bin/as --with-gmp=/usr/local --with-gxx-include-dir=/usr/local/lib/gcc8/include/c++/ --with-ld=/usr/local/bin/ld --with-pkgversion='FreeBSD Ports Collection' --with-system-zlib --enable-languages=c,c++,objc,fortran --prefix=/usr/local --localstatedir=/var --mandir=/usr/local/man --infodir=/usr/local/info/gcc8 --build=x86_64-portbld-freebsd10.3 Thread model: posix gcc version 8.0.0 20170820 (experimental) (FreeBSD Ports Collection) % git clone g...@github.com:ruby/ruby.git % cd ruby % autoconf % ./configure optflags='-O3 -Wl,-rpath=/usr/local/lib/gcc8' --with-gcc='/usr/local/bin/gcc8' % make miniruby % gdb --args ./miniruby -e'Thread.new("foo", &Object.method(:class_eval)).join' GNU gdb (GDB) 8.0 [GDB v8.0 for FreeBSD] Copyright (C) 2017 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-portbld-freebsd10.3". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from ./miniruby...done. (gdb) r Starting program: /tmp/ruby/miniruby -eThread.new\(\"foo\",\ \&Object.method\(:class_eval\)\).join [New LWP 101479 of process 94182] [New LWP 101478 of process 94182] Thread 2 received signal SIGSEGV, Segmentation fault. [Switching to LWP 101479 of process 94182] 0x000000000124c591 in rb_vm_get_ruby_level_next_cfp (cfp=0x805506000, th=<optimized out>) at vm.c:505 505 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); (gdb) disas 0x000000000124c591-61,+116 Dump of assembler code from 0x124c554 to 0x124c5c8: 0x000000000124c554 <eval_string_with_cref+116>: mov 0x60(%rsp),%rdx 0x000000000124c559 <eval_string_with_cref+121>: mov 0x28(%rdx),%rcx 0x000000000124c55d <eval_string_with_cref+125>: mov 0x20(%rdx),%rdx 0x000000000124c561 <eval_string_with_cref+129>: mov 0x30(%rax),%rax 0x000000000124c565 <eval_string_with_cref+133>: lea (%rdx,%rcx,8),%rcx 0x000000000124c569 <eval_string_with_cref+137>: cmp %rcx,%rax 0x000000000124c56c <eval_string_with_cref+140>: jae 0x124c59f <eval_string_with_cref+191> 0x000000000124c56e <eval_string_with_cref+142>: mov 0x20(%rax),%rdx 0x000000000124c572 <eval_string_with_cref+146>: testb $0x80,(%rdx) 0x000000000124c575 <eval_string_with_cref+149>: jne 0x124c589 <eval_string_with_cref+169> 0x000000000124c577 <eval_string_with_cref+151>: jmpq 0x124c6d8 <eval_string_with_cref+504> 0x000000000124c57c <eval_string_with_cref+156>: nopl 0x0(%rax) 0x000000000124c580 <eval_string_with_cref+160>: test %rdx,%rdx 0x000000000124c583 <eval_string_with_cref+163>: je 0x124c6d8 <eval_string_with_cref+504> 0x000000000124c589 <eval_string_with_cref+169>: add $0x30,%rax 0x000000000124c58d <eval_string_with_cref+173>: mov 0x20(%rax),%rdx => 0x000000000124c591 <eval_string_with_cref+177>: mov (%rdx),%rdx 0x000000000124c594 <eval_string_with_cref+180>: and $0x80,%edx 0x000000000124c59a <eval_string_with_cref+186>: cmp %rax,%rcx 0x000000000124c59d <eval_string_with_cref+189>: ja 0x124c580 <eval_string_with_cref+160> 0x000000000124c59f <eval_string_with_cref+191>: lea 0x306f5a(%rip),%rax # 0x1553500 <rb_eRuntimeError> 0x000000000124c5a6 <eval_string_with_cref+198>: lea 0x4a79b(%rip),%rsi # 0x1296d48 0x000000000124c5ad <eval_string_with_cref+205>: mov (%rax),%rdi 0x000000000124c5b0 <eval_string_with_cref+208>: xor %eax,%eax 0x000000000124c5b2 <eval_string_with_cref+210>: callq 0x10c4af0 <rb_raise> 0x000000000124c5b7 <eval_string_with_cref+215>: nopw 0x0(%rax,%rax,1) 0x000000000124c5c0 <eval_string_with_cref+224>: lea 0x2f3499(%rip),%rsi # 0x153fa60 <ruby_binding_data_type> 0x000000000124c5c7 <eval_string_with_cref+231>: mov %rax,%rdi End of assembler dump. Related macro expanded C source (vm.i) is as follows: ``` static rb_control_frame_t * vm_get_ruby_level_caller_cfp(const rb_thread_t *th, const rb_control_frame_t *cfp) { if (VM_FRAME_RUBYFRAME_P(cfp)) { return (rb_control_frame_t *)cfp; } cfp = ((cfp)+1); while (!(!((void *)(((rb_control_frame_t *)((th)->ec.vm_stack + (th)->ec.vm_stack_size))) > (void *)((cfp))))) { if (VM_FRAME_RUBYFRAME_P(cfp)) { return (rb_control_frame_t *)cfp; } if (VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_PASSED) == 0) { break; } cfp = ((cfp)+1); } return 0; } ``` As the vm.i file shows, it loops from 0x124c59d to 0x124c580. When cfp (%rax) become larger than `(th)->ec.vm_stack + (th)->ec.vm_stack_size` (%rcx), it escapes from while loop. But at that time, cfp may be pointed invalid structure. VM_FRAME_RUBYFRAME_P(cfp) accesses `cfp->ep[0]`. On the C source, it is safe while "while 'scondition is true => cfp->ep[0] is valid" is satisfied. But optimized asm compiled by GCC8 accesses `cfp->ep[0]` at 0x124c591 before checking while condition and jump at 0x124c59a,0x124c594. As far as I understand, such optimization is not allowed in C (especially on x86). Expected binary is like below: ``` 22e893: 48 8b 44 24 60 mov 0x60(%rsp),%rax 22e898: 48 8b 54 24 60 mov 0x60(%rsp),%rdx 22e89d: 48 8b 4a 28 mov 0x28(%rdx),%rcx 22e8a1: 48 8b 52 20 mov 0x20(%rdx),%rdx 22e8a5: 48 8b 40 30 mov 0x30(%rax),%rax 22e8a9: 48 8d 0c ca lea (%rdx,%rcx,8),%rcx 22e8ad: 48 39 c8 cmp %rcx,%rax 22e8b0: 72 27 jb 22e8d9 <eval_string_with_cref+0xb9> 22e8b2: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) 22e8b8: 48 8b 3d e1 42 30 00 mov 0x3042e1(%rip),%rdi # 532ba0 <rb_eRuntimeError > 22e8bf: 48 8d 35 4a ba 04 00 lea 0x4ba4a(%rip),%rsi # 27a310 <ruby_api_version+ 0x888> 22e8c6: 31 c0 xor %eax,%eax 22e8c8: e8 e3 76 e7 ff callq a5fb0 <rb_raise> 22e8cd: 0f 1f 00 nopl (%rax) 22e8d0: 48 83 c0 30 add $0x30,%rax 22e8d4: 48 39 c1 cmp %rax,%rcx 22e8d7: 76 df jbe 22e8b8 <eval_string_with_cref+0x98> 22e8d9: 48 8b 50 20 mov 0x20(%rax),%rdx 22e8dd: f6 02 80 testb $0x80,(%rdx) 22e8e0: 75 ee jne 22e8d0 <eval_string_with_cref+0xb0> 22e8e2: c5 fa 6f 40 18 vmovdqu 0x18(%rax),%xmm0 ``` It has cmp and be before accessing `0x20(%rax)`.