Skip to content

Segfault when using C compatible variadic functions #58980

Closed

Description

@thedataking

This example prints test 1 2 3 when compiled and run in debug mode:

#![feature(c_variadic)]

extern "C" {
    #[no_mangle]
    fn vprintf(_: *const libc::c_char, _: ...) -> libc::c_int;
}

#[no_mangle]
unsafe extern "C" fn variadic(fmt: *const libc::c_char, a: ...)
 -> libc::c_int {
    vprintf(fmt, a)
}

fn main() {
    let fmt = std::ffi::CString::new("test %d %d %d\n").expect("new failed");
    unsafe {
        variadic(fmt.as_ptr(), 1, 2, 3);
    }
}

Compiling and running in release mode causes a segfault in glibc/stdio-common/vfprintf.c:1286 because ap is nul. Enabling debug info in release mode via Cargo.toml makes the segfault go away.

Meta

$ rustc --version --verbose
rustc 1.35.0-nightly (f22dca0a1 2019-03-05)
binary: rustc
commit-hash: f22dca0a1bef4141e75326caacc3cd59f3d5be8e
commit-date: 2019-03-05
host: x86_64-unknown-linux-gnu
release: 1.35.0-nightly
LLVM version: 8.0

Activity

  1. dlrobertson commented on Mar 7, 2019

    @dlrobertson
    Contributor

    @thedataking thanks for reporting this. The example should probably look more like the following. The example still segfaults (even in debug mode).

    #![feature(c_variadic)]
    use std::ffi::VaList;
    
    extern "C" {
        // vprintf signature is `int vprintf(const char * format, va_list arg)`
        #[no_mangle]
        fn vprintf(_: *const libc::c_char, _: VaList) -> libc::c_int;
    }
    
    #[no_mangle]
    unsafe extern "C" fn variadic(fmt: *const libc::c_char, a: ...) -> libc::c_int {
        // `va_start` in theory should be automatically called giving you a
        // `VaList` at `a` here.
        vprintf(fmt, a)
        // `va_end` should be called here.
    }
    
    fn main() {
        let fmt = std::ffi::CString::new("test %d %d %d\n").expect("new failed");
        unsafe {
            variadic(fmt.as_ptr(), 1, 2, 3);
        }
    }

    If a is used in the function variadic the issue does not happen. Essentially we're optimizing variadic to the following

    define i32 @variadic(i8* %fmt, ...) unnamed_addr #3 personality i32 (i32, i32, i64, %"unwind::libunwind::_Unwind_Exception"*, %"unwind::libunwind::_Unwind_Context"*)* @rust_eh_personality {
    start:
      %personalityslot = alloca { i8*, i32 }, align 8
      %0 = invoke i32 @vprintf(i8* %fmt, i64* align 8 dereferenceable(24) undef)
              to label %bb2 unwind label %cleanup
      ...
    

    Note the missing va_start and va_end. I think this is happening because we're hitting src/librustc_codegen_ssa/mir/mod.rs:536-540. I'll do some more investigation shortly.

  2. dlrobertson commented on Mar 7, 2019

    @dlrobertson
    Contributor

    @thedataking the code I list in the above example should work after #58985 is merged.

  3. added
    A-FFIArea: Foreign function interface (FFI)
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    C-bugCategory: This is a bug.
    on Mar 7, 2019
  4. added a commit that references this issue on Mar 8, 2019
    b2ea6c8
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

    A-FFIArea: Foreign function interface (FFI)C-bugCategory: This is a bug.T-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