Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

format_args! with certain arguments segfaults at run-time on x86_64-unknown-linux-gnux32 #59220

Closed
tormol opened this issue Mar 15, 2019 · 8 comments
Labels
C-bug Category: This is a bug. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness O-x32 x32 ABI P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@tormol
Copy link
Contributor

tormol commented Mar 15, 2019

Trying to display most types causes a segmentation fault at run time when compiled in debug mode for x32 ABI

Example:

echo 'fn main() { println!("{}", 0); }' | rustc --target x86_64-unknown-linux-gnux32 -
./rust_out
Segmentation fault (core dumped)
  • println!, format! and format_args! equally affected, .to_string() is not.
  • Only happens in debug mode, works fine in release mode.
  • Happens with all integer types from i8 to u128, f32, f64, bool, char and fmt::Argument, but not with references to those types or str, String and raw pointers.
  • Debug and pointer formatting ("{:?}", "{:#?}" and "{:p}") are not affect, but all other format traits ("{:x}", "{:02}", "{:#b}"`) all result in a segfault.
Debugging attempt & assembly

The segfault happens at the second instruction in main;
here's what that area looks like with ... --emit asm:

_ZN8rust_out4main17h19287f9e3b324627E:
	.cfi_startproc
	subl	$72, %esp
	.cfi_def_cfa_offset 80
	movl	_ZN4core3fmt3num52_$LT$impl$u20$core..fmt..Display$u20$for$u20$i32$GT$3fmt17ha3b2ffd94f72c608E@GOTPCREL, %esi
	leal	.L__unnamed_2(%rip), %eax
	movl	%eax, 68(%esp)
	movl	68(%esp), %edi
	callq	_ZN4core3fmt10ArgumentV13new17hfeb1c719432004d8E
	movl	%eax, 28(%esp)
	movl	%edx, 24(%esp)
-- snip

and after disassembling: (objcopy -dr rust_out | rg -A10 'rust_out.*?main'

00002610 <_ZN8rust_out4main17h19287f9e3b324627E>:
    2610:	83 ec 48             	sub    $0x48,%esp
    2613:	8b 34 25 aa 49 22 00 	mov    0x2249aa,%esi
    261a:	8d 05 84 9a 01 00    	lea    0x19a84(%rip),%eax        # 1c0a4 <_fini+0x10>
    2620:	67 89 44 24 44       	mov    %eax,0x44(%esp)
    2625:	67 8b 7c 24 44       	mov    0x44(%esp),%edi
    262a:	e8 81 fe ff ff       	callq  24b0 <_ZN4core3fmt10ArgumentV13new17hfeb1c719432004d8E>
    262f:	67 89 44 24 1c       	mov    %eax,0x1c(%esp)
    2634:	67 89 54 24 18       	mov    %edx,0x18(%esp)
    2639:	8d 05 c1 36 22 00    	lea    0x2236c1(%rip),%eax        # 225d00 <debug_section_names+0x40>
    263f:	8d 0d 63 9a 01 00    	lea    0x19a63(%rip),%ecx        # 1c0a8 <_fini+0x14>
-- snip

With -O the compiler emits

72:_ZN8rust_out4main17h19287f9e3b324627E:
73-	.cfi_startproc
74-	subl	$40, %esp
75-	.cfi_def_cfa_offset 48
76-	leal	.L__unnamed_2(%rip), %eax
77-	movl	%eax, 8(%esp)
78-	movl	_ZN4core3fmt3num52_$LT$impl$u20$core..fmt..Display$u20$for$u20$i32$GT$3fmt17ha3b2ffd94f72c608E@GOTPCREL(%rip), %eax
79-	movl	%eax, 12(%esp)
80-	leal	.L__unnamed_3(%rip), %eax
81-	movl	%eax, 16(%esp)
82-	movl	$2, 20(%esp)
-- snip

For println!("{:?}", 0):

314:_ZN8rust_out4main17h19287f9e3b324627E:
315-	.cfi_startproc
316-	subl	$72, %esp
317-	.cfi_def_cfa_offset 80
318-	leal	.L__unnamed_2(%rip), %eax
319-	movl	%eax, 68(%esp)
320-	movl	68(%esp), %edi
321-	leal	_ZN4core3fmt3num50_$LT$impl$u20$core..fmt..Debug$u20$for$u20$i32$GT$3fmt17had225e6626567b7bE(%rip), %esi
322-	callq	_ZN4core3fmt10ArgumentV13new17hfeb1c719432004d8E
323-	movl	%eax, 28(%esp)
324-	movl	%edx, 24(%esp)
-- snip

and for println!("{}", &0)

268:_ZN8rust_out4main17h19287f9e3b324627E:
269-	.cfi_startproc
270-	subl	$72, %esp
271-	.cfi_def_cfa_offset 80
272-	leal	.L__unnamed_2(%rip), %eax
273-	movl	%eax, 68(%esp)
274-	movl	68(%esp), %edi
275-	leal	_ZN44_$LT$$RF$T$u20$as$u20$core..fmt..Display$GT$3fmt17h1aa6d8d315ee6bb6E(%rip), %esi
276-	callq	_ZN4core3fmt10ArgumentV13new17h122dce191cc0b572E
277-	movl	%eax, 28(%esp)
278-	movl	%edx, 24(%esp)
-- snip

Meta

rustc 1.33.0 (2aa4c46cf 2019-02-28)
binary: rustc
commit-hash: 2aa4c46cfdd726e97360c2734835aa3515e8c858
commit-date: 2019-02-28
host: x86_64-unknown-linux-gnu
release: 1.33.0
LLVM version: 8.0
@Mark-Simulacrum
Copy link
Member

cc @malbarbo who I believe initially added this target

Since this is not a tier 1 target I am not nominating for the compiler team to take a look (we don't have the resources to support it at this point).

@Mark-Simulacrum Mark-Simulacrum added the O-x32 x32 ABI label Mar 15, 2019
@Centril Centril added the I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness label Mar 16, 2019
@jonas-schievink jonas-schievink added C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 15, 2019
@Elinvynia Elinvynia added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Jun 9, 2020
@spastorino spastorino added P-medium Medium priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Jun 17, 2020
@alsoijw
Copy link

alsoijw commented Nov 7, 2020

Some additional information:
--emit=llvm-ir and --emit=llvm-bc aren't affected. They produced less code that rustc, so output binary is smaller.

$ objdump -d main | wc -l
38982
$ objdump -d 32 | wc -l
384
$
rm 32
rustc -C opt-level=0 -g --emit=llvm-ir main.rs -o x32.ll  --target x86_64-unknown-linux-gnux32
LLVM_HOME=/usr/lib/llvm/10/
$LLVM_HOME/bin/llvm-as x32.ll
$LLVM_HOME/bin/opt -load ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnux32/lib/libtest-1b777edb18de95a1.so -o x32.bc x32.bc
$LLVM_HOME/bin/llc -filetype=obj x32.bc
$LLVM_HOME/bin/clang -g -o 32 -mx32 x32.o -L ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnux32/lib/ -lstd-f6f0a67eb2dc7ef6
LD_LIBRARY_PATH=~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnux32/lib/ ./32 

rm 32
rustc main.rs -C opt-level=0 -o 32 -g --emit=llvm-ir -O -C no-prepopulate-passes -C codegen-units=1 --target x86_64-unknown-linux-gnux32 
$LLVM_HOME/bin/llvm-as x32.ll
$LLVM_HOME/bin/opt -load ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnux32/lib/libtest-1b777edb18de95a1.so -o x32.bc x32.bc
$LLVM_HOME/bin/llc -filetype=obj x32.bc
$LLVM_HOME/bin/clang -g -o 32 -mx32 x32.o -L ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnux32/lib/ -lstd-f6f0a67eb2dc7ef6
LD_LIBRARY_PATH=~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnux32/lib/ ./32 

rm 32
rustc -C opt-level=0 -g  --emit=llvm-bc main.rs -o x32.bc --target x86_64-unknown-linux-gnux32
$LLVM_HOME/bin/llc -filetype=obj x32.bc
$LLVM_HOME/bin/clang -g -o 32 -mx32 x32.o -L ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnux32/lib/ -lstd-f6f0a67eb2dc7ef6
LD_LIBRARY_PATH=~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnux32/lib/ ./32 

All this parts build x32 binary that work fine.

And debugging original binary builded from rustc with gdb. I set breakpoint on main and disassemble code. I check 4 variants with true, &true, "", 8. This is example for ""

gdb) b main
Breakpoint 1 at 0x40e0
(gdb) disas 0x40e0
Dump of assembler code for function main:
   0x000040e0 <+0>:     sub    $0x18,%esp
   0x000040e3 <+3>:     mov    0x26c88(%rip),%al        # 0x2ad71 <__rustc_debug_gdb_scripts_section__>
   0x000040e9 <+9>:     lea    -0x9f(%rip),%ecx        # 0x4050 <main::main>
   0x000040ef <+15>:    mov    %edi,0x14(%esp)
   0x000040f4 <+20>:    mov    %ecx,%edi
   0x000040f6 <+22>:    mov    0x14(%esp),%ecx
   0x000040fb <+27>:    mov    %esi,0x10(%esp)
   0x00004100 <+32>:    mov    %ecx,%esi
   0x00004102 <+34>:    mov    0x10(%esp),%edx
   0x00004107 <+39>:    mov    %al,0xf(%esp)
   0x0000410c <+44>:    callq  0x4190 <std::rt::lang_start>
   0x00004111 <+49>:    add    $0x18,%esp
   0x00004114 <+52>:    retq   
End of assembler dump.
(gdb) disas 0x4050
Dump of assembler code for function main::main:
   0x00004050 <+0>:     sub    $0x38,%esp
   0x00004053 <+3>:     mov    0x2ef5b(%rip),%eax        # 0x32fb4
   0x00004059 <+9>:     mov    0x2ef61(%rip),%ecx        # 0x32fc0
   0x0000405f <+15>:    mov    %ecx,0x30(%esp)
   0x00004064 <+20>:    mov    0x30(%esp),%ecx
   0x00004069 <+25>:    mov    %ecx,0x34(%esp)
   0x0000406e <+30>:    mov    %ecx,%edi
   0x00004070 <+32>:    lea    -0x116(%rip),%esi        # 0x3f60 <<&T as core::fmt::Display>::fmt>
   0x00004076 <+38>:    mov    %eax,0xc(%esp)
   0x0000407b <+43>:    callq  0x3fa0 <core::fmt::ArgumentV1::new>
   0x00004080 <+48>:    mov    %eax,0x8(%esp)
   0x00004085 <+53>:    mov    %edx,0x4(%esp)
   0x0000408a <+58>:    mov    0x8(%esp),%eax
   0x0000408f <+63>:    mov    %eax,0x28(%esp)
   0x00004094 <+68>:    mov    0x4(%esp),%ecx
   0x00004099 <+73>:    mov    %ecx,0x2c(%esp)
   0x0000409e <+78>:    lea    0x28(%rsp),%edx
   0x000040a2 <+82>:    lea    0x10(%rsp),%edi
   0x000040a6 <+86>:    mov    0xc(%esp),%esi
   0x000040ab <+91>:    mov    $0x2,%r8d
   0x000040b1 <+97>:    mov    %edx,(%esp)
   0x000040b5 <+101>:   mov    %r8d,%edx
   0x000040b8 <+104>:   mov    (%esp),%ecx
   0x000040bc <+108>:   mov    $0x1,%r8d
   0x000040c2 <+114>:   callq  0x4000 <core::fmt::Arguments::new_v1>
   0x000040c7 <+119>:   lea    0x10(%rsp),%edi
   0x000040cb <+123>:   callq  0x7b10 <std::io::stdio::_print>
   0x000040d0 <+128>:   add    $0x38,%esp
   0x000040d3 <+131>:   retq   
End of assembler dump.
(gdb) 

There are <<&T as core::fmt::Display>::fmt> in the beginning of main::main In correct binaries. But in corrupted there isn't.

(gdb) b main
Breakpoint 1 at 0x40a0
(gdb) disas 0x40a0
Dump of assembler code for function main:
   0x000040a0 <+0>:     sub    $0x18,%esp
   0x000040a3 <+3>:     mov    0x26ccc(%rip),%al        # 0x2ad75 <__rustc_debug_gdb_scripts_section__>
   0x000040a9 <+9>:     lea    -0x9f(%rip),%ecx        # 0x4010 <main::main>
   0x000040af <+15>:    mov    %edi,0x14(%esp)
   0x000040b4 <+20>:    mov    %ecx,%edi
   0x000040b6 <+22>:    mov    0x14(%esp),%ecx
   0x000040bb <+27>:    mov    %esi,0x10(%esp)
   0x000040c0 <+32>:    mov    %ecx,%esi
   0x000040c2 <+34>:    mov    0x10(%esp),%edx
   0x000040c7 <+39>:    mov    %al,0xf(%esp)
   0x000040cc <+44>:    callq  0x4150 <std::rt::lang_start>
   0x000040d1 <+49>:    add    $0x18,%esp
   0x000040d4 <+52>:    retq   
End of assembler dump.
(gdb) disas 0x4010
Dump of assembler code for function main::main:
   0x00004010 <+0>:     sub    $0x38,%esp
   0x00004013 <+3>:     mov    0x2ffd2,%esi
   0x0000401a <+10>:    mov    0x2ef94(%rip),%eax        # 0x32fb4
   0x00004020 <+16>:    mov    0x2ef92(%rip),%ecx        # 0x32fb8
   0x00004026 <+22>:    mov    %ecx,0x30(%esp)
   0x0000402b <+27>:    mov    0x30(%esp),%ecx
   0x00004030 <+32>:    mov    %ecx,0x34(%esp)
   0x00004035 <+37>:    mov    %ecx,%edi
   0x00004037 <+39>:    mov    %eax,0xc(%esp)
   0x0000403c <+44>:    callq  0x3f60 <core::fmt::ArgumentV1::new>
   0x00004041 <+49>:    mov    %eax,0x8(%esp)
   0x00004046 <+54>:    mov    %edx,0x4(%esp)
   0x0000404b <+59>:    mov    0x8(%esp),%eax
   0x00004050 <+64>:    mov    %eax,0x28(%esp)
   0x00004055 <+69>:    mov    0x4(%esp),%ecx
   0x0000405a <+74>:    mov    %ecx,0x2c(%esp)
   0x0000405f <+79>:    lea    0x28(%rsp),%edx
   0x00004063 <+83>:    lea    0x10(%rsp),%edi
   0x00004067 <+87>:    mov    0xc(%esp),%esi
   0x0000406c <+92>:    mov    $0x2,%r8d
   0x00004072 <+98>:    mov    %edx,(%esp)
   0x00004076 <+102>:   mov    %r8d,%edx
   0x00004079 <+105>:   mov    (%esp),%ecx
   0x0000407d <+109>:   mov    $0x1,%r8d
   0x00004083 <+115>:   callq  0x3fc0 <core::fmt::Arguments::new_v1>
   0x00004088 <+120>:   lea    0x10(%rsp),%edi
   0x0000408c <+124>:   callq  0x7ad0 <std::io::stdio::_print>
   0x00004091 <+129>:   add    $0x38,%esp
   0x00004094 <+132>:   retq   
End of assembler dump.
(gdb) 

@hvdijk
Copy link
Contributor

hvdijk commented Feb 6, 2021

This should be fixed automatically the next time rust updates to a newer LLVM. I have recently committed patches to fix x32 codegen to upstream LLVM and am locally running rust with those same patches applied. The test case in this issue works fine here, as do various bigger rust programs such as ripgrep, and with a few trivial patches, rustc itself.

@alsoijw
Copy link

alsoijw commented Feb 15, 2021

and with a few trivial patches, rustc itself.

@hvdijk this is about rustc_data_structures::static_assert_size?

@hvdijk
Copy link
Contributor

hvdijk commented Feb 15, 2021

and with a few trivial patches, rustc itself.

@hvdijk this is about rustc_data_structures::static_assert_size?

Yes, those are the only modifications needed on the Rust side of things (as opposed to the LLVM side) to get a working x32 rustc binary. The static_assert_size!s are conditional on #[cfg(target_arch = "x86_64")] and should be conditional on #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]. I am planning on submitting PRs for that once the LLVM side of things is ready.

@alsoijw
Copy link

alsoijw commented Feb 27, 2021

@hvdijk can you say version of llvm or hash with fix?

@hvdijk
Copy link
Contributor

hvdijk commented Feb 27, 2021

@hvdijk can you say version of llvm or hash with fix?

LLVM 12 will have the fixes.

@malbarbo
Copy link
Contributor

malbarbo commented Mar 5, 2021

#81451 was merged and I checked that this issue was indeed fixed.

@sanxiyn sanxiyn closed this as completed Mar 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness O-x32 x32 ABI P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

10 participants