Skip to content

Odd segfault when copying data #7413

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

Closed
hoelzro opened this issue Jun 26, 2013 · 3 comments
Closed

Odd segfault when copying data #7413

hoelzro opened this issue Jun 26, 2013 · 3 comments
Labels
I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics.

Comments

@hoelzro
Copy link
Contributor

hoelzro commented Jun 26, 2013

Hi Rust devs,

I wrote up a program in Rust to do some stuff with ptrace, but it segfaults (a backtrace shows a memcpy variant). Here's a link to a slightly more minimal example:

https://gist.github.com/hoelzro/5871030

@emberian
Copy link
Member

Cannot reproduce with:

use std::io;
use ptrace::word;

mod posix {
    use std::libc;
    use std::os;
    use std::ptr;
    use std::str;
    use std::vec;

    mod c {
        use std::libc;

        extern {
            fn fork() -> libc::pid_t;
            fn exit(status: libc::c_int) -> !;
            fn waitpid(pid: libc::pid_t, status: *libc::c_int, flags: libc::c_int) -> libc::c_int;
            fn execvp(file: *libc::c_char, argv: **libc::c_char) -> !;
        }
    }

    pub enum ForkResult {
        ForkFailure(int),
        ForkChild,
        ForkParent(int),
    }

    pub enum WaitPidResult {
        WaitPidFailure(int),
        WaitPidSuccess(int, int),
    }

    pub fn fork() -> ForkResult {
        unsafe {
            let pid = c::fork();

            match pid {
                -1 => ForkFailure(os::errno()),
                0 => ForkChild,
                pid => ForkParent(pid as int),
            }
        }
    }

    pub fn waitpid(pid: int, flags: int) -> WaitPidResult {
        unsafe {
            let status : libc::c_int = 0;

            let pid = c::waitpid(pid as libc::pid_t, &status as *libc::c_int, flags as libc::c_int);

            if pid == -1 {
                WaitPidFailure(os::errno())
            } else {
                WaitPidSuccess(pid as int, status as int)
            }
        }
    }

    // this is probably pretty awful...
    fn str_array_to_char_pp(ary: &[~str], callback: &fn(**libc::c_char)) {
        fn helper_fn(ptrs: &mut ~[*libc::c_char], ary: &[~str], callback: &fn(**libc::c_char)) {
            match ary {
                [] => {
                    ptrs.push(ptr::null());
                    callback(vec::raw::to_ptr(*ptrs));
                },
                    [ref head, ..tail] => {
                        do head.to_c_str().with_ref() |raw_str| {
                            ptrs.push(raw_str);
                            helper_fn(ptrs, tail, |c| callback(c));
                        }
                    },
            }
        }

        let mut ptrs : ~[*libc::c_char] = vec::with_capacity(ary.len());

        helper_fn(&mut ptrs, ary, callback);
    }

    pub fn exec(command_and_args: &[~str]) {
        unsafe {
            do command_and_args[0].to_c_str().with_ref() |command| {
                do str_array_to_char_pp(command_and_args) |args| {
                    c::execvp(command, args);
                }
            }
        }
    }

    pub fn exit(status: int) -> ! {
        unsafe {
            c::exit(status as libc::c_int)
        }
    }

    pub static SIGTRAP : int = 5;
}

mod ptrace {
    use std::libc;
    use std::ptr;

    extern {
        fn ptrace(request: libc::c_int, pid: libc::pid_t, addr: *libc::c_void, data: *libc::c_void) -> libc::c_long;
    }

    pub type word = u64;

    static TRACEME : libc::c_int = 0;
    static SYSCALL : libc::c_int = 24;
    static SETOPTIONS : libc::c_int = 0x4200;

    pub fn trace_me() {
        unsafe {
            ptrace(TRACEME, 0, ptr::null(), ptr::null());
        }
    }

    pub fn setoptions(pid: int, options: int) {
        unsafe {
            ptrace(SETOPTIONS, pid as libc::pid_t, ptr::null(), options as *libc::c_void);
        }
    }

    pub fn syscall(pid: int) {
        unsafe {
            ptrace(SYSCALL, pid as libc::pid_t, ptr::null(), ptr::null());
        }
    }

    pub static TRACESYSGOOD : int = 0x00000001;
    pub static TRACEFORK : int = 0x00000002;
    pub static TRACEEXEC : int = 0x00000010;
}

enum TraceEvent {
    SystemCall {
        syscall_no : word,
        arguments : (word, word, word, word, word, word),
    },
    Other,
}

fn init_trace(child_pid: int) {
    match posix::waitpid(child_pid, 0) {
        posix::WaitPidFailure(_) => (),
        posix::WaitPidSuccess(pid, status) => {
            if status & posix::SIGTRAP != 0 {
                ptrace::setoptions(pid, ptrace::TRACEFORK | ptrace::TRACESYSGOOD | ptrace::TRACEEXEC);
                ptrace::syscall(pid);
            }
        },
    }
}

fn resume_trace(child_pid: int) {
    ptrace::syscall(child_pid);
}

fn next_trace(callback: &fn(int, TraceEvent)) -> bool {
    loop {
        let result = posix::waitpid(-1, 0);

        match result {
            posix::WaitPidFailure(_) => return false,
            posix::WaitPidSuccess(pid, status) => {
                if ((status >> 8) & (0x80 | posix::SIGTRAP)) != 0 {
                    callback(pid, SystemCall {
                             syscall_no: 0,
                             arguments: ( 0, 0, 0, 0, 0, 0 ),
                             });
                } else {
                    callback(pid, Other);
                }
                resume_trace(pid);
            },
        }
    }
}

fn run_parent(child_pid: int) {
    init_trace(child_pid);

    do next_trace() |_, event| {
        match event {
            // XXX changing _args to _ fixes the segfault (?!?!?!)
            SystemCall { syscall_no: _, arguments: _args } => {
            }
            _ => (),
        };
    };
}

fn main() {
    let result = posix::fork();

    match result {
        posix::ForkChild => {
            ptrace::trace_me();

            posix::exec([~"ls"]);
            posix::exit(255);
        }
        posix::ForkFailure(_) => {
            io::println("An error occurred");
        }
        posix::ForkParent(child_pid) => {
            run_parent(child_pid);
        }
    }
}

@hoelzro can you confirm?

@alexcrichton
Copy link
Member

Closing because I think that this is done now. A fair amount of work was done awhile back to fix match and related cases.

If you continue to run into problems though, feel free to comment/re-open!

@hoelzro
Copy link
Contributor Author

hoelzro commented Sep 7, 2013

@alexcrichton Thanks for following up on this; I had forgotten that I'd created this issue! I have "ported" my code to Rust HEAD, and it now runs without segfaults.

flip1995 pushed a commit to flip1995/rust that referenced this issue Sep 3, 2021
Manual map 7413

fixes: rust-lang#7413

This only fixes the specific problem from rust-lang#7413, not the general case. The full fix requires interacting with the borrow checker to determine the lifetime of all the borrows made in the function. I'll open an issue about it later.

changelog: Don't suggest using `map` when the option is borrowed in the match, and also consumed in the arm.
changelog: Locals declared within the would-be closure will not prevent the closure from being suggested in `manual_map` and `map_entry`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics.
Projects
None yet
Development

No branches or pull requests

3 participants