[Bug c/81954] New: gcc8 too aggressively reorders memory access beyond condition
naruse at airemix dot jp
gcc-bugzilla@gcc.gnu.org
Wed Aug 23 20:03:00 GMT 2017
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 git@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)`.
More information about the Gcc-bugs
mailing list