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)`.

Reply via email to