Bug 10723 – std.stdio.File.byLine causes segfault when compiling with -O
Status
RESOLVED
Resolution
WORKSFORME
Severity
major
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2013-07-27T17:23:00Z
Last change time
2014-07-15T05:49:00Z
Keywords
wrong-code
Assigned to
nobody
Creator
hsteoh
Comments
Comment #0 by hsteoh — 2013-07-27T17:23:36Z
Code:
------
import std.stdio;
void main() {
try {
auto f = File("/non/existent/file").byLine();
} catch(Exception e) {
writeln(e.msg);
}
}
------
Compile command:
------
dmd -O test.d
------
Program output:
------
Segmentation fault
------
Compiling without -O doesn't exhibit this problem.
Here's the stacktrace from gdb:
#0 0x0000000000440110 in std.stdio.File.detach() ()
#1 0x000000000043fe09 in std.stdio.File.__dtor() ()
#2 0x000000000043179a in D main ()
#3 0x000000000043c854 in rt.dmain2._d_run_main() ()
#4 0x000000000043c0de in rt.dmain2._d_run_main() ()
#5 0x000000000043c8b0 in rt.dmain2._d_run_main() ()
#6 0x000000000043c0de in rt.dmain2._d_run_main() ()
#7 0x000000000043c09a in _d_run_main ()
#8 0x000000000043bea0 in main ()
Looks like -O is causing the compiler to generate wrong code in this case.
Comment #1 by hsteoh — 2013-07-27T17:27:27Z
P.S. The bug doesn't happen if the try-catch block is removed, or if the File struct is assigned to a temporary variable first, then byLine called on it.
Comment #2 by maxim — 2013-08-08T07:10:32Z
This is a variation of issue 9438
Gdb reports 0x0000000000444a99 in std.stdio.File.detach() (this=0x435e2e <D main+130>)
at std/stdio.d:426
426 if (_p.refs == 1)
Note that this parameter is 0x435e2e <D main+130> which means that this struct pointer points to executable code instead of stack. Looking to _Dmain function:
Dump of assembler code for function _Dmain:
0x0000000000435dac <+0>: push %rbp
0x0000000000435dad <+1>: mov %rsp,%rbp
0x0000000000435db0 <+4>: sub $0x78,%rsp
0x0000000000435db4 <+8>: push %rbx
0x0000000000435db5 <+9>: push %r12
0x0000000000435db7 <+11>: push %r13
0x0000000000435db9 <+13>: push %r14
0x0000000000435dbb <+15>: push %r15
0x0000000000435dbd <+17>: xor %ecx,%ecx
0x0000000000435dbf <+19>: mov $0xa,%edx
0x0000000000435dc4 <+24>: lea -0x48(%rbp),%rsi
0x0000000000435dc8 <+28>: mov %rcx,-0x70(%rbp)
0x0000000000435dcc <+32>: mov %rdx,-0x68(%rbp)
0x0000000000435dd0 <+36>: mov %rsi,-0x60(%rbp)
0x0000000000435dd4 <+40>: mov 0x288ad(%rip),%rdx # 0x45e688 <_TMP1+8>
0x0000000000435ddb <+47>: mov 0x2889e(%rip),%rax # 0x45e680 <_TMP1>
0x0000000000435de2 <+54>: mov %rax,%rcx
0x0000000000435de5 <+57>: mov %rdx,%r8
0x0000000000435de8 <+60>: mov 0x288b9(%rip),%rdx # 0x45e6a8 <_TMP2+8>
---Type <return> to continue, or q <return> to quit---
0x0000000000435def <+67>: mov 0x288aa(%rip),%rsi # 0x45e6a0 <_TMP2>
0x0000000000435df6 <+74>: lea -0x18(%rbp),%rbx
0x0000000000435dfa <+78>: xor %rdi,%rdi
0x0000000000435dfd <+81>: mov %rdi,(%rbx)
0x0000000000435e00 <+84>: mov %rdi,0x8(%rbx)
0x0000000000435e04 <+88>: mov %rdi,0x10(%rbx)
0x0000000000435e08 <+92>: mov %rbx,%rdi
0x0000000000435e0b <+95>: callq 0x444464 <_D3std5stdio4File6__ctorMFNcAyaxAaZS3std5stdio4File>
0x0000000000435e10 <+100>: mov %rax,%rdi
0x0000000000435e13 <+103>: mov -0x70(%rbp),%rcx
0x0000000000435e17 <+107>: mov -0x68(%rbp),%rdx
0x0000000000435e1b <+111>: mov -0x60(%rbp),%rsi
0x0000000000435e1f <+115>: callq 0x4378d4 <_D3std5stdio4File15__T6byLineTaTaZ6byLineMFE3std6string14KeepTerminatoraZS3std5stdio4File15__T6ByLineTaTaZ6ByLine>
0x0000000000435e24 <+120>: mov %rax,%r12
0x0000000000435e27 <+123>: callq 0x435e2e <_Dmain+130>
0x0000000000435e2c <+128>: jmp 0x435e37 <_Dmain+139>
0x0000000000435e2e <+130>: mov %rbx,%rdi
0x0000000000435e31 <+133>: callq 0x44455c <_D3std5stdio4File6__dtorMFZv>
0x0000000000435e36 <+138>: retq
---Type <return> to continue, or q <return> to quit---
0x0000000000435e37 <+139>: lea -0x48(%rbp),%rdi
0x0000000000435e3b <+143>: callq 0x437c7c <_D3std5stdio4File15__T6ByLineTaTaZ6ByLine11__fieldDtorMFZv>
0x0000000000435e40 <+148>: jmp 0x435e56 <_Dmain+170>
0x0000000000435e42 <+150>: mov -0x58(%rbp),%rax
0x0000000000435e46 <+154>: mov 0x18(%rax),%rdx
0x0000000000435e4a <+158>: mov 0x10(%rax),%rdi
0x0000000000435e4e <+162>: mov %rdx,%rsi
0x0000000000435e51 <+165>: callq 0x4388f4 <_D3std5stdio16__T7writelnTAyaZ7writelnFAyaZv>
0x0000000000435e56 <+170>: xor %eax,%eax
0x0000000000435e58 <+172>: pop %r15
0x0000000000435e5a <+174>: pop %r14
0x0000000000435e5c <+176>: pop %r13
0x0000000000435e5e <+178>: pop %r12
0x0000000000435e60 <+180>: pop %rbx
0x0000000000435e61 <+181>: mov %rbp,%rsp
0x0000000000435e64 <+184>: pop %rbp
0x0000000000435e65 <+185>: retq
End of assembler dump.
gives that _Dmain+130 is instruction next to which druntime should upwind stack in order to store in $rdi valid this struct pointer for further dtor calling.
The reason is probably another (https://github.com/D-Programming-Language/dmd/pull/1645) bug in except_fillInEHTable() which incorrectly calculates sizes of instruction and stores wrong offset in deh tables. In runtime exception code upwinds up to next to correct instruction, so $rdi does not keep right pointer and program later segfaults.
Comment #3 by maxim — 2013-08-15T10:53:46Z
Reduced:
struct File
{
private struct Impl
{
uint refs = uint.max / 2;
}
private Impl* _p;
private string _name;
this(string name, in char[] stdioOpenmode = "rb")
{
_p = new Impl();
_p.refs = 1;
throw new Exception(name);
}
~this() {
assert(_p.refs);
--_p.refs;
_p = null;
}
int byLine() {
return 0;
}
}
void main() {
try {
int f = File("It's OK").byLine();
} catch(Exception e) { }
}
Interesting is that if _name member or in char[] stdioOpenmode = "rb" are removed, the program runs fine.
Comment #4 by maxim — 2013-08-15T12:17:26Z
(In reply to comment #3)
> Interesting is that if _name member or in char[] stdioOpenmode = "rb" are
> removed, the program runs fine.
This is actually a funny bug - in char[] stdioOpenmode = "rb" shouldn't compile in first place, this is accept-invalid. This erroneous default parameter as a result causes further problems with dehtables as offsets are wrong.
https://github.com/D-Programming-Language/phobos/pull/1478
(In reply to comment #4)
> (In reply to comment #3)
>
> > Interesting is that if _name member or in char[] stdioOpenmode = "rb" are
> > removed, the program runs fine.
>
> This is actually a funny bug - in char[] stdioOpenmode = "rb" shouldn't compile
> in first place, this is accept-invalid. This erroneous default parameter as a
> result causes further problems with dehtables as offsets are wrong.
>
> https://github.com/D-Programming-Language/phobos/pull/1478
Why is this invalid? I thought 'in char[]' just means const(char[]), which immutable(char)[] should be implicitly convertible to.
Comment #7 by maxim — 2013-08-15T12:49:25Z
(In reply to comment #6)
> (In reply to comment #4)
> > (In reply to comment #3)
> >
> > > Interesting is that if _name member or in char[] stdioOpenmode = "rb" are
> > > removed, the program runs fine.
> >
> > This is actually a funny bug - in char[] stdioOpenmode = "rb" shouldn't compile
> > in first place, this is accept-invalid. This erroneous default parameter as a
> > result causes further problems with dehtables as offsets are wrong.
> >
> > https://github.com/D-Programming-Language/phobos/pull/1478
>
> Why is this invalid? I thought 'in char[]' just means const(char[]), which
> immutable(char)[] should be implicitly convertible to.
Yes.
Comment #8 by hsteoh — 2013-11-26T16:55:17Z
*** Issue 11611 has been marked as a duplicate of this issue. ***
Comment #9 by hsteoh — 2013-11-26T17:01:39Z
@Maxim: one of your comments mentioned a new dmd bug that you filed, but the link points back to this bug. What's the bug number of the dmd issue?
Comment #10 by maxim — 2013-11-26T21:22:59Z
(In reply to comment #9)
> @Maxim: one of your comments mentioned a new dmd bug that you filed, but the
> link points back to this bug. What's the bug number of the dmd issue?
I meant https://github.com/D-Programming-Language/dmd/pull/1645
and respectively issue 9438
Comment #11 by hsteoh — 2013-11-27T07:12:08Z
Pasting the slightly different test case from 11611:
-----
import std.stdio;
void func(R)(R input) { }
version=bad;
void main()
{
try
{
version(bad)
func(File("").byLine());
version(good)
{
auto f = File("");
func(f.byLine());
}
}
catch(Exception e)
{
}
}
-----