Skip to content

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

Closed

Description

@tormol

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

Activity

  1. Mark-Simulacrum commented on Mar 15, 2019

    @Mark-Simulacrum
    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).

  2. added
    I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
    on Mar 16, 2019
  3. added
    C-bugCategory: This is a bug.
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Sep 15, 2019
  4. added
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Jun 9, 2020
  5. added and removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Jun 17, 2020
  6. alsoijw commented on Nov 7, 2020

    @alsoijw

    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) 
    
  7. hvdijk commented on Feb 6, 2021

    @hvdijk
    Contributor

    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.

  8. alsoijw commented on Feb 15, 2021

    @alsoijw

    and with a few trivial patches, rustc itself.

    @hvdijk this is about rustc_data_structures::static_assert_size?

  9. hvdijk commented on Feb 15, 2021

    @hvdijk
    Contributor

    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.

  10. alsoijw commented on Feb 27, 2021

    @alsoijw

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

  11. hvdijk commented on Feb 27, 2021

    @hvdijk
    Contributor

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

    LLVM 12 will have the fixes.

  12. malbarbo commented on Mar 5, 2021

    @malbarbo
    Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessO-x3232 bit ABI for x86-64P-mediumMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions