Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing Vec<String> from Rust to char** in C

I've been trying to write a shell in Rust that links directly to the libc library. I've used a Vec<String> to hold the arguments to be passed to execvp(), but it seems that my conversion to char ** has not been successful. Upon execution, all the parameters became null strings.

Here's the piece of code involved.

fn safe_execvp(path: String, argv: Vec<String>) -> Result<(), i32> {
    unsafe {
        let c_path = CString::new(path.as_str()).unwrap();
        let mut c_argv_vec = Vec::new();
        for arg in &argv {
            let c_arg = CString::new(arg.as_str()).unwrap().as_ptr();
            c_argv_vec.push(c_arg);
        }
        c_argv_vec.push(std::ptr::null());
        match execvp(c_file.as_ptr(), c_argv_vec.as_ptr()) {
            num => Err(num),
        }
    }
}

execvp is the C library function defined as fn execvp(file: *const i8, argv: *const*const i8) -> i32;.

I'm not sure what I've done wrong. Is it because the memory for the arguments were released before calling execvp()?

like image 257
Jianzhong Liu Avatar asked Dec 06 '22 15:12

Jianzhong Liu


1 Answers

You are creating CString instances and immediately fetching a pointer. Consequently, as you well guessed, ownership of this string was dropped prematurely. This is similar to returning a reference to a local instance, except that since pointers do not retain lifetime information, this case does not trigger a compilation error.

The solution to your problem is to retain the owned C-style strings during the function's scope, and produce a pointer to pointers of the same content separately.

let cstr_argv: Vec<_> = argv.iter()
        .map(|arg| CString::new(arg.as_str()).unwrap())
        .collect();

let mut p_argv: Vec<_> = cstr_argv.iter() // do NOT into_iter()
        .map(|arg| arg.as_ptr())
        .collect();

p_argv.push(std::ptr::null());

let p: *const *const c_char = p_argv.as_ptr();

Playground.

See also: CString::new().unwrap().as_ptr() gives empty *const c_char

like image 61
E_net4 stands with Ukraine Avatar answered Jan 18 '23 07:01

E_net4 stands with Ukraine