Skip to content

Commit cfaf500

Browse files
committed
Add NULL check in argument parsing on Apple platforms
1 parent 4350550 commit cfaf500

File tree

1 file changed

+44
-14
lines changed

1 file changed

+44
-14
lines changed

std/src/sys/pal/unix/args.rs

+44-14
Original file line numberDiff line numberDiff line change
@@ -184,28 +184,58 @@ mod imp {
184184
#[cfg(target_vendor = "apple")]
185185
mod imp {
186186
use super::Args;
187-
use crate::ffi::CStr;
187+
use crate::ffi::{c_char, c_int, CStr};
188188
use crate::os::unix::prelude::*;
189189

190-
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
190+
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
191+
// No need to initialize anything in here, `libdyld.dylib` has already
192+
// done the work for us.
193+
}
191194

192195
pub fn args() -> Args {
193196
extern "C" {
194197
// These functions are in crt_externs.h.
195-
fn _NSGetArgc() -> *mut libc::c_int;
196-
fn _NSGetArgv() -> *mut *mut *mut libc::c_char;
198+
fn _NSGetArgc() -> *mut c_int;
199+
fn _NSGetArgv() -> *mut *mut *mut c_char;
200+
}
201+
202+
// SAFETY: The returned pointer points to a static initialized early
203+
// in the program lifetime by `libdyld.dylib`, and as such is always
204+
// valid.
205+
//
206+
// NOTE: Similar to `_NSGetEnviron`, there technically isn't anything
207+
// protecting us against concurrent modifications to this, and there
208+
// doesn't exist a lock that we can take. Instead, it is generally
209+
// expected that it's only modified in `main` / before other code
210+
// runs, so reading this here should be fine.
211+
let argc = unsafe { _NSGetArgc().read() };
212+
// SAFETY: Same as above.
213+
let argv = unsafe { _NSGetArgv().read() };
214+
215+
let mut vec = Vec::with_capacity(argc as usize);
216+
217+
for i in 0..argc {
218+
// SAFETY: `argv` is at least as long as `argc`, so reading from
219+
// it should be safe.
220+
let ptr = unsafe { argv.offset(i as isize).read() };
221+
222+
// Entries may have been removed from `argv` by setting them to
223+
// NULL, without updating `argc`.
224+
if ptr.is_null() {
225+
// We continue instead of break here, as an argument may have
226+
// been set to `NULL` in the middle, instead of at the end of
227+
// the list.
228+
//
229+
// This is the same as what `-[NSProcessInfo arguments]` does.
230+
continue;
231+
}
232+
233+
// SAFETY: Just checked that the pointer is not NULL, and
234+
// arguments are otherwise guaranteed to be valid C strings.
235+
let cstr = unsafe { CStr::from_ptr(ptr) };
236+
vec.push(OsStringExt::from_vec(cstr.to_bytes().to_vec()));
197237
}
198238

199-
let vec = unsafe {
200-
let (argc, argv) =
201-
(*_NSGetArgc() as isize, *_NSGetArgv() as *const *const libc::c_char);
202-
(0..argc as isize)
203-
.map(|i| {
204-
let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec();
205-
OsStringExt::from_vec(bytes)
206-
})
207-
.collect::<Vec<_>>()
208-
};
209239
Args { iter: vec.into_iter() }
210240
}
211241
}

0 commit comments

Comments
 (0)