diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs index ff64d2aa82515..29fc0835d7666 100644 --- a/library/std/src/sys/common/mod.rs +++ b/library/std/src/sys/common/mod.rs @@ -11,3 +11,7 @@ #![allow(dead_code)] pub mod alloc; +pub mod small_c_string; + +#[cfg(test)] +mod tests; diff --git a/library/std/src/sys/common/small_c_string.rs b/library/std/src/sys/common/small_c_string.rs new file mode 100644 index 0000000000000..01acd5191351c --- /dev/null +++ b/library/std/src/sys/common/small_c_string.rs @@ -0,0 +1,58 @@ +use crate::ffi::{CStr, CString}; +use crate::mem::MaybeUninit; +use crate::path::Path; +use crate::slice; +use crate::{io, ptr}; + +// Make sure to stay under 4096 so the compiler doesn't insert a probe frame: +// https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html +#[cfg(not(target_os = "espidf"))] +const MAX_STACK_ALLOCATION: usize = 384; +#[cfg(target_os = "espidf")] +const MAX_STACK_ALLOCATION: usize = 32; + +const NUL_ERR: io::Error = + io::const_io_error!(io::ErrorKind::InvalidInput, "file name contained an unexpected NUL byte"); + +#[inline] +pub fn run_path_with_cstr(path: &Path, f: F) -> io::Result +where + F: FnOnce(&CStr) -> io::Result, +{ + run_with_cstr(path.as_os_str().bytes(), f) +} + +#[inline] +pub fn run_with_cstr(bytes: &[u8], f: F) -> io::Result +where + F: FnOnce(&CStr) -> io::Result, +{ + if bytes.len() >= MAX_STACK_ALLOCATION { + return run_with_cstr_allocating(bytes, f); + } + + let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit(); + let buf_ptr = buf.as_mut_ptr() as *mut u8; + + unsafe { + ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len()); + buf_ptr.add(bytes.len()).write(0); + } + + match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) { + Ok(s) => f(s), + Err(_) => Err(NUL_ERR), + } +} + +#[cold] +#[inline(never)] +fn run_with_cstr_allocating(bytes: &[u8], f: F) -> io::Result +where + F: FnOnce(&CStr) -> io::Result, +{ + match CString::new(bytes) { + Ok(s) => f(&s), + Err(_) => Err(NUL_ERR), + } +} diff --git a/library/std/src/sys/common/tests.rs b/library/std/src/sys/common/tests.rs new file mode 100644 index 0000000000000..fb6f5d6af8371 --- /dev/null +++ b/library/std/src/sys/common/tests.rs @@ -0,0 +1,66 @@ +use crate::ffi::CString; +use crate::hint::black_box; +use crate::path::Path; +use crate::sys::common::small_c_string::run_path_with_cstr; +use core::iter::repeat; + +#[test] +fn stack_allocation_works() { + let path = Path::new("abc"); + let result = run_path_with_cstr(path, |p| { + assert_eq!(p, &*CString::new(path.as_os_str().bytes()).unwrap()); + Ok(42) + }); + assert_eq!(result.unwrap(), 42); +} + +#[test] +fn stack_allocation_fails() { + let path = Path::new("ab\0"); + assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err()); +} + +#[test] +fn heap_allocation_works() { + let path = repeat("a").take(384).collect::(); + let path = Path::new(&path); + let result = run_path_with_cstr(path, |p| { + assert_eq!(p, &*CString::new(path.as_os_str().bytes()).unwrap()); + Ok(42) + }); + assert_eq!(result.unwrap(), 42); +} + +#[test] +fn heap_allocation_fails() { + let mut path = repeat("a").take(384).collect::(); + path.push('\0'); + let path = Path::new(&path); + assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err()); +} + +#[bench] +fn bench_stack_path_alloc(b: &mut test::Bencher) { + let path = repeat("a").take(383).collect::(); + let p = Path::new(&path); + b.iter(|| { + run_path_with_cstr(p, |cstr| { + black_box(cstr); + Ok(()) + }) + .unwrap(); + }); +} + +#[bench] +fn bench_heap_path_alloc(b: &mut test::Bencher) { + let path = repeat("a").take(384).collect::(); + let p = Path::new(&path); + b.iter(|| { + run_path_with_cstr(p, |cstr| { + black_box(cstr); + Ok(()) + }) + .unwrap(); + }); +} diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index f921839cf529f..af297ff1ec75b 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -1,3 +1,4 @@ +use crate::convert::TryFrom; use crate::ffi::{CStr, CString, OsString}; use crate::fmt; use crate::hash::{Hash, Hasher}; @@ -5,6 +6,7 @@ use crate::io::{self, Error, ErrorKind}; use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::os::unix::ffi::OsStrExt; use crate::path::{Path, PathBuf}; +use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::cvt; use crate::sys::hermit::abi; use crate::sys::hermit::abi::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY}; @@ -15,10 +17,6 @@ use crate::sys::unsupported; pub use crate::sys_common::fs::{copy, try_exists}; //pub use crate::sys_common::fs::remove_dir_all; -fn cstr(path: &Path) -> io::Result { - Ok(CString::new(path.as_os_str().as_bytes())?) -} - #[derive(Debug)] pub struct File(FileDesc); @@ -272,8 +270,7 @@ impl OpenOptions { impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let path = cstr(path)?; - File::open_c(&path, opts) + run_path_with_cstr(path, |path| File::open_c(&path, opts)) } pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result { @@ -373,9 +370,7 @@ pub fn readdir(_p: &Path) -> io::Result { } pub fn unlink(path: &Path) -> io::Result<()> { - let name = cstr(path)?; - let _ = unsafe { cvt(abi::unlink(name.as_ptr()))? }; - Ok(()) + run_path_with_cstr(path, |path| cvt(unsafe { abi::unlink(path.as_ptr()) }).map(|_| ())) } pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 167c918c94cf9..c080c176a2ace 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -22,7 +22,7 @@ #![allow(missing_debug_implementations)] -mod common; +pub mod common; cfg_if::cfg_if! { if #[cfg(unix)] { diff --git a/library/std/src/sys/solid/os.rs b/library/std/src/sys/solid/os.rs index eecb347e599ef..4906c62689d4c 100644 --- a/library/std/src/sys/solid/os.rs +++ b/library/std/src/sys/solid/os.rs @@ -1,4 +1,5 @@ use super::unsupported; +use crate::convert::TryFrom; use crate::error::Error as StdError; use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; @@ -9,6 +10,7 @@ use crate::os::{ }; use crate::path::{self, PathBuf}; use crate::sync::RwLock; +use crate::sys::common::small_c_string::run_with_cstr; use crate::vec; use super::{error, itron, memchr}; @@ -139,35 +141,33 @@ pub fn env() -> Env { pub fn getenv(k: &OsStr) -> Option { // environment variables with a nul byte can't be set, so their value is // always None as well - let k = CString::new(k.as_bytes()).ok()?; - unsafe { + let s = run_with_cstr(k.as_bytes(), |k| { let _guard = ENV_LOCK.read(); - let s = libc::getenv(k.as_ptr()) as *const libc::c_char; - if s.is_null() { - None - } else { - Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec())) - } + Ok(unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char) + }) + .ok()?; + + if s.is_null() { + None + } else { + Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec())) } } pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let k = CString::new(k.as_bytes())?; - let v = CString::new(v.as_bytes())?; - - unsafe { - let _guard = ENV_LOCK.write(); - cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - } + run_with_cstr(k.as_bytes(), |k| { + run_with_cstr(v.as_bytes(), |v| { + let _guard = ENV_LOCK.write(); + cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) + }) + }) } pub fn unsetenv(n: &OsStr) -> io::Result<()> { - let nbuf = CString::new(n.as_bytes())?; - - unsafe { + run_with_cstr(n.as_bytes(), |nbuf| { let _guard = ENV_LOCK.write(); - cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop) - } + cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) + }) } /// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 57c7bf6a28b90..fa03233c86ba8 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1,6 +1,6 @@ use crate::os::unix::prelude::*; -use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem; @@ -8,6 +8,7 @@ use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; use crate::path::{Path, PathBuf}; use crate::ptr; use crate::sync::Arc; +use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::fd::FileDesc; use crate::sys::time::SystemTime; use crate::sys::{cvt, cvt_r}; @@ -260,7 +261,7 @@ pub struct DirEntry { // We need to store an owned copy of the entry name on platforms that use // readdir() (not readdir_r()), because a) struct dirent may use a flexible // array to store the name, b) it lives only until the next readdir() call. - name: CString, + name: crate::ffi::CString, } // Define a minimal subset of fields we need from `dirent64`, especially since @@ -900,8 +901,7 @@ impl OpenOptions { impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let path = cstr(path)?; - File::open_c(&path, opts) + run_path_with_cstr(path, |path| File::open_c(path, opts)) } pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result { @@ -1114,9 +1114,7 @@ impl DirBuilder { } pub fn mkdir(&self, p: &Path) -> io::Result<()> { - let p = cstr(p)?; - cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?; - Ok(()) + run_path_with_cstr(p, |p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ())) } pub fn set_mode(&mut self, mode: u32) { @@ -1124,10 +1122,6 @@ impl DirBuilder { } } -fn cstr(path: &Path) -> io::Result { - Ok(CString::new(path.as_os_str().as_bytes())?) -} - impl AsInner for File { fn as_inner(&self) -> &FileDesc { &self.0 @@ -1273,173 +1267,170 @@ impl fmt::Debug for File { } } -pub fn readdir(p: &Path) -> io::Result { - let root = p.to_path_buf(); - let p = cstr(p)?; - unsafe { - let ptr = libc::opendir(p.as_ptr()); - if ptr.is_null() { - Err(Error::last_os_error()) - } else { - let inner = InnerReadDir { dirp: Dir(ptr), root }; - Ok(ReadDir { - inner: Arc::new(inner), - #[cfg(not(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", - target_os = "fuchsia", - target_os = "redox", - )))] - end_of_stream: false, - }) - } +pub fn readdir(path: &Path) -> io::Result { + let ptr = run_path_with_cstr(path, |p| unsafe { Ok(libc::opendir(p.as_ptr())) })?; + if ptr.is_null() { + Err(Error::last_os_error()) + } else { + let root = path.to_path_buf(); + let inner = InnerReadDir { dirp: Dir(ptr), root }; + Ok(ReadDir { + inner: Arc::new(inner), + #[cfg(not(any( + target_os = "android", + target_os = "linux", + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox", + )))] + end_of_stream: false, + }) } } pub fn unlink(p: &Path) -> io::Result<()> { - let p = cstr(p)?; - cvt(unsafe { libc::unlink(p.as_ptr()) })?; - Ok(()) + run_path_with_cstr(p, |p| cvt(unsafe { libc::unlink(p.as_ptr()) }).map(|_| ())) } pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - let old = cstr(old)?; - let new = cstr(new)?; - cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?; - Ok(()) + run_path_with_cstr(old, |old| { + run_path_with_cstr(new, |new| { + cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ()) + }) + }) } pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { - let p = cstr(p)?; - cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?; - Ok(()) + run_path_with_cstr(p, |p| cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ())) } pub fn rmdir(p: &Path) -> io::Result<()> { - let p = cstr(p)?; - cvt(unsafe { libc::rmdir(p.as_ptr()) })?; - Ok(()) + run_path_with_cstr(p, |p| cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ())) } pub fn readlink(p: &Path) -> io::Result { - let c_path = cstr(p)?; - let p = c_path.as_ptr(); + run_path_with_cstr(p, |c_path| { + let p = c_path.as_ptr(); - let mut buf = Vec::with_capacity(256); + let mut buf = Vec::with_capacity(256); - loop { - let buf_read = - cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize; + loop { + let buf_read = + cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? + as usize; - unsafe { - buf.set_len(buf_read); - } + unsafe { + buf.set_len(buf_read); + } - if buf_read != buf.capacity() { - buf.shrink_to_fit(); + if buf_read != buf.capacity() { + buf.shrink_to_fit(); - return Ok(PathBuf::from(OsString::from_vec(buf))); - } + return Ok(PathBuf::from(OsString::from_vec(buf))); + } - // Trigger the internal buffer resizing logic of `Vec` by requiring - // more space than the current capacity. The length is guaranteed to be - // the same as the capacity due to the if statement above. - buf.reserve(1); - } + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. The length is guaranteed to be + // the same as the capacity due to the if statement above. + buf.reserve(1); + } + }) } pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { - let original = cstr(original)?; - let link = cstr(link)?; - cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) })?; - Ok(()) + run_path_with_cstr(original, |original| { + run_path_with_cstr(link, |link| { + cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ()) + }) + }) } pub fn link(original: &Path, link: &Path) -> io::Result<()> { - let original = cstr(original)?; - let link = cstr(link)?; - cfg_if::cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon"))] { - // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves - // it implementation-defined whether `link` follows symlinks, so rely on the - // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. - // Android has `linkat` on newer versions, but we happen to know `link` - // always has the correct behavior, so it's here as well. - cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; - } else if #[cfg(target_os = "macos")] { - // On MacOS, older versions (<=10.9) lack support for linkat while newer - // versions have it. We want to use linkat if it is available, so we use weak! - // to check. `linkat` is preferable to `link` because it gives us a flag to - // specify how symlinks should be handled. We pass 0 as the flags argument, - // meaning it shouldn't follow symlinks. - weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int); - - if let Some(f) = linkat.get() { - cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; - } else { - cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; - }; - } else { - // Where we can, use `linkat` instead of `link`; see the comment above - // this one for details on why. - cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; - } - } - Ok(()) + run_path_with_cstr(original, |original| { + run_path_with_cstr(link, |link| { + cfg_if::cfg_if! { + if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon"))] { + // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves + // it implementation-defined whether `link` follows symlinks, so rely on the + // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. + // Android has `linkat` on newer versions, but we happen to know `link` + // always has the correct behavior, so it's here as well. + cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; + } else if #[cfg(target_os = "macos")] { + // On MacOS, older versions (<=10.9) lack support for linkat while newer + // versions have it. We want to use linkat if it is available, so we use weak! + // to check. `linkat` is preferable to `link` because it gives us a flag to + // specify how symlinks should be handled. We pass 0 as the flags argument, + // meaning it shouldn't follow symlinks. + weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int); + + if let Some(f) = linkat.get() { + cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; + } else { + cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; + }; + } else { + // Where we can, use `linkat` instead of `link`; see the comment above + // this one for details on why. + cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; + } + } + Ok(()) + }) + }) } pub fn stat(p: &Path) -> io::Result { - let p = cstr(p)?; - - cfg_has_statx! { - if let Some(ret) = unsafe { try_statx( - libc::AT_FDCWD, - p.as_ptr(), - libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, - ) } { - return ret; + run_path_with_cstr(p, |p| { + cfg_has_statx! { + if let Some(ret) = unsafe { try_statx( + libc::AT_FDCWD, + p.as_ptr(), + libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_ALL, + ) } { + return ret; + } } - } - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?; - Ok(FileAttr::from_stat64(stat)) + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?; + Ok(FileAttr::from_stat64(stat)) + }) } pub fn lstat(p: &Path) -> io::Result { - let p = cstr(p)?; - - cfg_has_statx! { - if let Some(ret) = unsafe { try_statx( - libc::AT_FDCWD, - p.as_ptr(), - libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, - ) } { - return ret; + run_path_with_cstr(p, |p| { + cfg_has_statx! { + if let Some(ret) = unsafe { try_statx( + libc::AT_FDCWD, + p.as_ptr(), + libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_ALL, + ) } { + return ret; + } } - } - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?; - Ok(FileAttr::from_stat64(stat)) + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?; + Ok(FileAttr::from_stat64(stat)) + }) } pub fn canonicalize(p: &Path) -> io::Result { - let path = CString::new(p.as_os_str().as_bytes())?; - let buf; - unsafe { - let r = libc::realpath(path.as_ptr(), ptr::null_mut()); - if r.is_null() { - return Err(io::Error::last_os_error()); - } - buf = CStr::from_ptr(r).to_bytes().to_vec(); - libc::free(r as *mut _); + let r = run_path_with_cstr(p, |path| unsafe { + Ok(libc::realpath(path.as_ptr(), ptr::null_mut())) + })?; + if r.is_null() { + return Err(io::Error::last_os_error()); } - Ok(PathBuf::from(OsString::from_vec(buf))) + Ok(PathBuf::from(OsString::from_vec(unsafe { + let buf = CStr::from_ptr(r).to_bytes().to_vec(); + libc::free(r as *mut _); + buf + }))) } fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { @@ -1589,9 +1580,9 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { // Opportunistically attempt to create a copy-on-write clone of `from` // using `fclonefileat`. if HAS_FCLONEFILEAT.load(Ordering::Relaxed) { - let to = cstr(to)?; - let clonefile_result = - cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) }); + let clonefile_result = run_path_with_cstr(to, |to| { + cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) }) + }); match clonefile_result { Ok(_) => return Ok(reader_metadata.len()), Err(err) => match err.raw_os_error() { @@ -1635,9 +1626,10 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { } pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { - let path = cstr(path)?; - cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?; - Ok(()) + run_path_with_cstr(path, |path| { + cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) + .map(|_| ()) + }) } pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> { @@ -1646,16 +1638,15 @@ pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> { } pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { - let path = cstr(path)?; - cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?; - Ok(()) + run_path_with_cstr(path, |path| { + cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) + .map(|_| ()) + }) } #[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] pub fn chroot(dir: &Path) -> io::Result<()> { - let dir = cstr(dir)?; - cvt(unsafe { libc::chroot(dir.as_ptr()) })?; - Ok(()) + run_path_with_cstr(dir, |dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ())) } pub use remove_dir_impl::remove_dir_all; @@ -1669,13 +1660,14 @@ mod remove_dir_impl { // Modern implementation using openat(), unlinkat() and fdopendir() #[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri)))] mod remove_dir_impl { - use super::{cstr, lstat, Dir, DirEntry, InnerReadDir, ReadDir}; + use super::{lstat, Dir, DirEntry, InnerReadDir, ReadDir}; use crate::ffi::CStr; use crate::io; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}; use crate::os::unix::prelude::{OwnedFd, RawFd}; use crate::path::{Path, PathBuf}; use crate::sync::Arc; + use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::{cvt, cvt_r}; #[cfg(not(all(target_os = "macos", not(target_arch = "aarch64")),))] @@ -1842,7 +1834,7 @@ mod remove_dir_impl { if attr.file_type().is_symlink() { crate::fs::remove_file(p) } else { - remove_dir_all_recursive(None, &cstr(p)?) + run_path_with_cstr(p, |p| remove_dir_all_recursive(None, &p)) } } diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 7c70ddbd9b98b..2f2663db60769 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -7,6 +7,7 @@ mod tests; use crate::os::unix::prelude::*; +use crate::convert::TryFrom; use crate::error::Error as StdError; use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; @@ -18,6 +19,7 @@ use crate::ptr; use crate::slice; use crate::str; use crate::sync::{PoisonError, RwLock}; +use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; use crate::sys::cvt; use crate::sys::fd; use crate::sys::memchr; @@ -170,12 +172,8 @@ pub fn chdir(p: &path::Path) -> io::Result<()> { #[cfg(not(target_os = "espidf"))] pub fn chdir(p: &path::Path) -> io::Result<()> { - let p: &OsStr = p.as_ref(); - let p = CString::new(p.as_bytes())?; - if unsafe { libc::chdir(p.as_ptr()) } != 0 { - return Err(io::Error::last_os_error()); - } - Ok(()) + let result = run_path_with_cstr(p, |p| unsafe { Ok(libc::chdir(p.as_ptr())) })?; + if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) } } pub struct SplitPaths<'a> { @@ -548,35 +546,32 @@ pub fn env() -> Env { pub fn getenv(k: &OsStr) -> Option { // environment variables with a nul byte can't be set, so their value is // always None as well - let k = CString::new(k.as_bytes()).ok()?; - unsafe { + let s = run_with_cstr(k.as_bytes(), |k| { let _guard = env_read_lock(); - let s = libc::getenv(k.as_ptr()) as *const libc::c_char; - if s.is_null() { - None - } else { - Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec())) - } + Ok(unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char) + }) + .ok()?; + if s.is_null() { + None + } else { + Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec())) } } pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let k = CString::new(k.as_bytes())?; - let v = CString::new(v.as_bytes())?; - - unsafe { - let _guard = ENV_LOCK.write(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - } + run_with_cstr(k.as_bytes(), |k| { + run_with_cstr(v.as_bytes(), |v| { + let _guard = ENV_LOCK.write(); + cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) + }) + }) } pub fn unsetenv(n: &OsStr) -> io::Result<()> { - let nbuf = CString::new(n.as_bytes())?; - - unsafe { + run_with_cstr(n.as_bytes(), |nbuf| { let _guard = ENV_LOCK.write(); - cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - } + cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) + }) } #[cfg(not(target_os = "espidf"))] diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 56bb71b5dcbde..7df4add8ce112 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -154,9 +154,8 @@ impl Thread { #[cfg(target_os = "netbsd")] pub fn set_name(name: &CStr) { - use crate::ffi::CString; - let cname = CString::new(&b"%s"[..]).unwrap(); unsafe { + let cname = CStr::from_bytes_with_nul_unchecked(b"%s\0".as_slice()); libc::pthread_setname_np( libc::pthread_self(), cname.as_ptr(), diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index 953fbeb8395ef..d4866bbc32bad 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -1,7 +1,7 @@ #![deny(unsafe_op_in_unsafe_fn)] use super::fd::WasiFd; -use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::iter; @@ -12,6 +12,7 @@ use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd use crate::path::{Path, PathBuf}; use crate::ptr; use crate::sync::Arc; +use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::time::SystemTime; use crate::sys::unsupported; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -694,51 +695,52 @@ fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result { /// Note that this can fail if `p` doesn't look like it can be opened relative /// to any pre-opened file descriptor. fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { - let p = CString::new(p.as_os_str().as_bytes())?; - let mut buf = Vec::::with_capacity(512); - loop { - unsafe { - let mut relative_path = buf.as_ptr().cast(); - let mut abs_prefix = ptr::null(); - let fd = __wasilibc_find_relpath( - p.as_ptr(), - &mut abs_prefix, - &mut relative_path, - buf.capacity(), - ); - if fd == -1 { - if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) { - // Trigger the internal buffer resizing logic of `Vec` by requiring - // more space than the current capacity. - let cap = buf.capacity(); - buf.set_len(cap); - buf.reserve(1); - continue; - } - let msg = format!( - "failed to find a pre-opened file descriptor \ - through which {:?} could be opened", - p + run_path_with_cstr(p, |p| { + let mut buf = Vec::::with_capacity(512); + loop { + unsafe { + let mut relative_path = buf.as_ptr().cast(); + let mut abs_prefix = ptr::null(); + let fd = __wasilibc_find_relpath( + p.as_ptr(), + &mut abs_prefix, + &mut relative_path, + buf.capacity(), ); - return Err(io::Error::new(io::ErrorKind::Uncategorized, msg)); - } - let relative = CStr::from_ptr(relative_path).to_bytes().to_vec(); + if fd == -1 { + if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) { + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. + let cap = buf.capacity(); + buf.set_len(cap); + buf.reserve(1); + continue; + } + let msg = format!( + "failed to find a pre-opened file descriptor \ + through which {:?} could be opened", + p + ); + return Err(io::Error::new(io::ErrorKind::Uncategorized, msg)); + } + let relative = CStr::from_ptr(relative_path).to_bytes().to_vec(); - return Ok(( - ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)), - PathBuf::from(OsString::from_vec(relative)), - )); + return Ok(( + ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)), + PathBuf::from(OsString::from_vec(relative)), + )); + } } - } - extern "C" { - pub fn __wasilibc_find_relpath( - path: *const libc::c_char, - abs_prefix: *mut *const libc::c_char, - relative_path: *mut *const libc::c_char, - relative_path_len: libc::size_t, - ) -> libc::c_int; - } + extern "C" { + pub fn __wasilibc_find_relpath( + path: *const libc::c_char, + abs_prefix: *mut *const libc::c_char, + relative_path: *mut *const libc::c_char, + relative_path_len: libc::size_t, + ) -> libc::c_int; + } + }) } pub fn osstr2str(f: &OsStr) -> io::Result<&str> { diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs index c5229a188342a..cab2887dfcf14 100644 --- a/library/std/src/sys/wasi/os.rs +++ b/library/std/src/sys/wasi/os.rs @@ -2,13 +2,14 @@ use crate::any::Any; use crate::error::Error as StdError; -use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; use crate::io; use crate::marker::PhantomData; use crate::os::wasi::prelude::*; use crate::path::{self, PathBuf}; use crate::str; +use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; use crate::sys::memchr; use crate::sys::unsupported; use crate::vec; @@ -77,13 +78,10 @@ pub fn getcwd() -> io::Result { } pub fn chdir(p: &path::Path) -> io::Result<()> { - let p: &OsStr = p.as_ref(); - let p = CString::new(p.as_bytes())?; - unsafe { - match libc::chdir(p.as_ptr()) == (0 as libc::c_int) { - true => Ok(()), - false => Err(io::Error::last_os_error()), - } + let result = run_path_with_cstr(p, |p| unsafe { Ok(libc::chdir(p.as_ptr())) })?; + match result == (0 as libc::c_int) { + true => Ok(()), + false => Err(io::Error::last_os_error()), } } @@ -176,35 +174,32 @@ pub fn env() -> Env { } pub fn getenv(k: &OsStr) -> Option { - let k = CString::new(k.as_bytes()).ok()?; - unsafe { + let s = run_with_cstr(k.as_bytes(), |k| unsafe { let _guard = env_lock(); - let s = libc::getenv(k.as_ptr()) as *const libc::c_char; - if s.is_null() { - None - } else { - Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec())) - } + Ok(libc::getenv(k.as_ptr()) as *const libc::c_char) + }) + .ok()?; + if s.is_null() { + None + } else { + Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec())) } } pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let k = CString::new(k.as_bytes())?; - let v = CString::new(v.as_bytes())?; - - unsafe { - let _guard = env_lock(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - } + run_with_cstr(k.as_bytes(), |k| { + run_with_cstr(v.as_bytes(), |v| unsafe { + let _guard = env_lock(); + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) + }) + }) } pub fn unsetenv(n: &OsStr) -> io::Result<()> { - let nbuf = CString::new(n.as_bytes())?; - - unsafe { + run_with_cstr(n.as_bytes(), |nbuf| unsafe { let _guard = env_lock(); cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - } + }) } pub fn temp_dir() -> PathBuf { diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 3ad802afa8f7a..fad4a63331b59 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -2,12 +2,13 @@ mod tests; use crate::cmp; -use crate::ffi::CString; +use crate::convert::{TryFrom, TryInto}; use crate::fmt; use crate::io::{self, ErrorKind, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::ptr; +use crate::sys::common::small_c_string::run_with_cstr; use crate::sys::net::netc as c; use crate::sys::net::{cvt, cvt_gai, cvt_r, init, wrlen_t, Socket}; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -197,14 +198,15 @@ impl<'a> TryFrom<(&'a str, u16)> for LookupHost { fn try_from((host, port): (&'a str, u16)) -> io::Result { init(); - let c_host = CString::new(host)?; - let mut hints: c::addrinfo = unsafe { mem::zeroed() }; - hints.ai_socktype = c::SOCK_STREAM; - let mut res = ptr::null_mut(); - unsafe { - cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) - .map(|_| LookupHost { original: res, cur: res, port }) - } + run_with_cstr(host.as_bytes(), |c_host| { + let mut hints: c::addrinfo = unsafe { mem::zeroed() }; + hints.ai_socktype = c::SOCK_STREAM; + let mut res = ptr::null_mut(); + unsafe { + cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) + .map(|_| LookupHost { original: res, cur: res, port }) + } + }) } } diff --git a/src/tools/miri/tests/fail/shims/fs/isolated_file.stderr b/src/tools/miri/tests/fail/shims/fs/isolated_file.stderr index 4e3fdc7a45801..269b1383aad68 100644 --- a/src/tools/miri/tests/fail/shims/fs/isolated_file.stderr +++ b/src/tools/miri/tests/fail/shims/fs/isolated_file.stderr @@ -10,6 +10,9 @@ LL | let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode a = note: inside closure at RUSTLIB/std/src/sys/PLATFORM/fs.rs:LL:CC = note: inside `std::sys::PLATFORM::cvt_r::` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC = note: inside `std::sys::PLATFORM::fs::File::open_c` at RUSTLIB/std/src/sys/PLATFORM/fs.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/PLATFORM/fs.rs:LL:CC + = note: inside `std::sys::PLATFORM::small_c_string::run_with_cstr::` at RUSTLIB/std/src/sys/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::PLATFORM::small_c_string::run_path_with_cstr::` at RUSTLIB/std/src/sys/PLATFORM/small_c_string.rs:LL:CC = note: inside `std::sys::PLATFORM::fs::File::open` at RUSTLIB/std/src/sys/PLATFORM/fs.rs:LL:CC = note: inside `std::fs::OpenOptions::_open` at RUSTLIB/std/src/fs.rs:LL:CC = note: inside `std::fs::OpenOptions::open::<&std::path::Path>` at RUSTLIB/std/src/fs.rs:LL:CC