Skip to content

Commit dedd430

Browse files
committed
Auto merge of #25641 - geofft:execve-const, r=alexcrichton
The `execv` family of functions and `getopt` are prototyped somewhat strangely in C: they take a `char *const argv[]` (and `envp`, for `execve`), an immutable array of mutable C strings -- in other words, a `char *const *argv` or `argv: *const *mut c_char`. The current Rust binding uses `*mut *const c_char`, which is backwards (a mutable array of constant C strings). That said, these functions do not actually modify their arguments. Once upon a time, C didn't have `const`, and to this day, string literals in C have type `char *` (`*mut c_char`). So an array of string literals has type `char * []`, equivalent to `char **` in a function parameter (Rust `*mut *mut c_char`). C allows an implicit cast from `T **` to `T * const *` (`*const *mut T`) but not to `const T * const *` (`*const *const T`). Therefore, prototyping `execv` as taking `const char * const argv[]` would have broken existing code (by requiring an explicit cast), despite being more correct. So, even though these functions don't need mutable data, they're prototyped as if they do. While it's theoretically possible that an implementation could choose to use its freedom to modify the mutable data, such an implementation would break the innumerable users of `execv`-family functions that call them with string literals. Such an implementation would also break `std::process`, which currently works around this with an unsafe `as *mut _` cast, and assumes that `execvp` secretly does not modify its argument. Furthermore, there's nothing useful to be gained by being able to write to the strings in `argv` themselves but not being able to write to the array containing those strings (you can't reorder arguments, add arguments, increase the length of any parameter, etc.). So, despite the C prototype with its legacy C problems, it's simpler for everyone for Rust to consider these functions as taking `*const *const c_char`, and it's also safe to do so. Rust does not need to expose the mistakes of C here. This patch makes that change, and drops the unsafe cast in `std::process` since it's now unnecessary.
2 parents a38e758 + 058a0f0 commit dedd430

File tree

2 files changed

+19
-17
lines changed

2 files changed

+19
-17
lines changed

src/liblibc/lib.rs

+18-16
Original file line numberDiff line numberDiff line change
@@ -5493,17 +5493,17 @@ pub mod funcs {
54935493
pub fn dup2(src: c_int, dst: c_int) -> c_int;
54945494
#[link_name = "_execv"]
54955495
pub fn execv(prog: *const c_char,
5496-
argv: *mut *const c_char) -> intptr_t;
5496+
argv: *const *const c_char) -> intptr_t;
54975497
#[link_name = "_execve"]
5498-
pub fn execve(prog: *const c_char, argv: *mut *const c_char,
5499-
envp: *mut *const c_char)
5498+
pub fn execve(prog: *const c_char, argv: *const *const c_char,
5499+
envp: *const *const c_char)
55005500
-> c_int;
55015501
#[link_name = "_execvp"]
55025502
pub fn execvp(c: *const c_char,
5503-
argv: *mut *const c_char) -> c_int;
5503+
argv: *const *const c_char) -> c_int;
55045504
#[link_name = "_execvpe"]
5505-
pub fn execvpe(c: *const c_char, argv: *mut *const c_char,
5506-
envp: *mut *const c_char) -> c_int;
5505+
pub fn execvpe(c: *const c_char, argv: *const *const c_char,
5506+
envp: *const *const c_char) -> c_int;
55075507
#[link_name = "_getcwd"]
55085508
pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char;
55095509
#[link_name = "_getpid"]
@@ -5687,12 +5687,12 @@ pub mod funcs {
56875687
pub fn dup(fd: c_int) -> c_int;
56885688
pub fn dup2(src: c_int, dst: c_int) -> c_int;
56895689
pub fn execv(prog: *const c_char,
5690-
argv: *mut *const c_char) -> c_int;
5691-
pub fn execve(prog: *const c_char, argv: *mut *const c_char,
5692-
envp: *mut *const c_char)
5690+
argv: *const *const c_char) -> c_int;
5691+
pub fn execve(prog: *const c_char, argv: *const *const c_char,
5692+
envp: *const *const c_char)
56935693
-> c_int;
56945694
pub fn execvp(c: *const c_char,
5695-
argv: *mut *const c_char) -> c_int;
5695+
argv: *const *const c_char) -> c_int;
56965696
pub fn fork() -> pid_t;
56975697
pub fn fpathconf(filedes: c_int, name: c_int) -> c_long;
56985698
pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char;
@@ -5702,7 +5702,9 @@ pub mod funcs {
57025702
pub fn getgroups(ngroups_max: c_int, groups: *mut gid_t)
57035703
-> c_int;
57045704
pub fn getlogin() -> *mut c_char;
5705-
pub fn getopt(argc: c_int, argv: *mut *const c_char,
5705+
// GNU getopt(3) modifies its arguments despite the
5706+
// char * const [] prototype; see the manpage.
5707+
pub fn getopt(argc: c_int, argv: *mut *mut c_char,
57065708
optstr: *const c_char) -> c_int;
57075709
pub fn getpgrp() -> pid_t;
57085710
pub fn getpid() -> pid_t;
@@ -5752,19 +5754,19 @@ pub mod funcs {
57525754
pub fn dup(fd: c_int) -> c_int;
57535755
pub fn dup2(src: c_int, dst: c_int) -> c_int;
57545756
pub fn execv(prog: *const c_char,
5755-
argv: *mut *const c_char) -> c_int;
5756-
pub fn execve(prog: *const c_char, argv: *mut *const c_char,
5757-
envp: *mut *const c_char)
5757+
argv: *const *const c_char) -> c_int;
5758+
pub fn execve(prog: *const c_char, argv: *const *const c_char,
5759+
envp: *const *const c_char)
57585760
-> c_int;
57595761
pub fn execvp(c: *const c_char,
5760-
argv: *mut *const c_char) -> c_int;
5762+
argv: *const *const c_char) -> c_int;
57615763
pub fn fork() -> pid_t;
57625764
pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char;
57635765
pub fn getegid() -> gid_t;
57645766
pub fn geteuid() -> uid_t;
57655767
pub fn getgid() -> gid_t;
57665768
pub fn getlogin() -> *mut c_char;
5767-
pub fn getopt(argc: c_int, argv: *mut *const c_char,
5769+
pub fn getopt(argc: c_int, argv: *const *const c_char,
57685770
optstr: *const c_char) -> c_int;
57695771
pub fn getuid() -> uid_t;
57705772
pub fn getsid(pid: pid_t) -> pid_t;

src/libstd/sys/unix/process.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ impl Process {
313313
if !envp.is_null() {
314314
*sys::os::environ() = envp as *const _;
315315
}
316-
let _ = libc::execvp(*argv, argv as *mut _);
316+
let _ = libc::execvp(*argv, argv);
317317
fail(&mut output)
318318
}
319319

0 commit comments

Comments
 (0)