diff options
Diffstat (limited to 'vendor/rustix/src/procfs.rs')
-rw-r--r-- | vendor/rustix/src/procfs.rs | 114 |
1 files changed, 75 insertions, 39 deletions
diff --git a/vendor/rustix/src/procfs.rs b/vendor/rustix/src/procfs.rs index 14ad631a7..fb35a2028 100644 --- a/vendor/rustix/src/procfs.rs +++ b/vendor/rustix/src/procfs.rs @@ -18,20 +18,19 @@ //! namespace. So with the checking here, they may fail, but they won't be able //! to succeed with bogus results. -use crate::backend::pid::syscalls::getpid; use crate::fd::{AsFd, BorrowedFd, OwnedFd}; +use crate::ffi::CStr; use crate::fs::{ - fstat, fstatfs, major, openat, renameat, FileType, FsWord, Mode, OFlags, Stat, CWD, + fstat, fstatfs, major, openat, renameat, FileType, FsWord, Mode, OFlags, RawDir, Stat, CWD, PROC_SUPER_MAGIC, }; use crate::io; use crate::path::DecInt; #[cfg(feature = "rustc-dep-of-std")] use core::lazy::OnceCell; +use core::mem::MaybeUninit; #[cfg(not(feature = "rustc-dep-of-std"))] use once_cell::sync::OnceCell; -#[cfg(feature = "alloc")] -use {crate::ffi::CStr, crate::fs::Dir}; /// Linux's procfs always uses inode 1 for its root directory. const PROC_ROOT_INO: u64 = 1; @@ -42,8 +41,8 @@ enum Kind { Proc, Pid, Fd, - #[cfg(feature = "alloc")] File, + Symlink, } /// Check a subdirectory of "/proc" for anomalies. @@ -69,16 +68,23 @@ fn check_proc_entry_with_stat( match kind { Kind::Proc => check_proc_root(entry, &entry_stat)?, Kind::Pid | Kind::Fd => check_proc_subdir(entry, &entry_stat, proc_stat)?, - #[cfg(feature = "alloc")] Kind::File => check_proc_file(&entry_stat, proc_stat)?, + Kind::Symlink => check_proc_symlink(&entry_stat, proc_stat)?, } // "/proc" directories are typically mounted r-xr-xr-x. // "/proc/self/fd" is r-x------. Allow them to have fewer permissions, but // not more. - let expected_mode = if let Kind::Fd = kind { 0o500 } else { 0o555 }; - if entry_stat.st_mode & 0o777 & !expected_mode != 0 { - return Err(io::Errno::NOTSUP); + match kind { + Kind::Symlink => { + // On Linux, symlinks don't have their own permissions. + } + _ => { + let expected_mode = if let Kind::Fd = kind { 0o500 } else { 0o555 }; + if entry_stat.st_mode & 0o777 & !expected_mode != 0 { + return Err(io::Errno::NOTSUP); + } + } } match kind { @@ -97,7 +103,6 @@ fn check_proc_entry_with_stat( return Err(io::Errno::NOTSUP); } } - #[cfg(feature = "alloc")] Kind::File => { // Check that files in procfs don't have extraneous hard links to // them (which might indicate hard links to other things). @@ -105,6 +110,13 @@ fn check_proc_entry_with_stat( return Err(io::Errno::NOTSUP); } } + Kind::Symlink => { + // Check that symlinks in procfs don't have extraneous hard links + // to them (which might indicate hard links to other things). + if entry_stat.st_nlink != 1 { + return Err(io::Errno::NOTSUP); + } + } } Ok(entry_stat) @@ -153,7 +165,6 @@ fn check_proc_subdir( Ok(()) } -#[cfg(feature = "alloc")] fn check_proc_file(stat: &Stat, proc_stat: Option<&Stat>) -> io::Result<()> { // Check that we have a regular file. if FileType::from_raw_mode(stat.st_mode) != FileType::RegularFile { @@ -165,6 +176,17 @@ fn check_proc_file(stat: &Stat, proc_stat: Option<&Stat>) -> io::Result<()> { Ok(()) } +fn check_proc_symlink(stat: &Stat, proc_stat: Option<&Stat>) -> io::Result<()> { + // Check that we have a symbolic link. + if FileType::from_raw_mode(stat.st_mode) != FileType::Symlink { + return Err(io::Errno::NOTSUP); + } + + check_proc_nonroot(stat, proc_stat)?; + + Ok(()) +} + fn check_proc_nonroot(stat: &Stat, proc_stat: Option<&Stat>) -> io::Result<()> { // Check that we haven't been linked back to the root of "/proc". if stat.st_ino == PROC_ROOT_INO { @@ -245,6 +267,7 @@ fn proc() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html +#[allow(unsafe_code)] fn proc_self() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { static PROC_SELF: StaticFd = StaticFd::new(); @@ -253,11 +276,21 @@ fn proc_self() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { .get_or_try_init(|| { let (proc, proc_stat) = proc()?; - let pid = getpid(); + // `getpid` would return our pid in our own pid namespace, so + // instead use `readlink` on the `self` symlink to learn our pid in + // the procfs namespace. + let self_symlink = open_and_check_file(proc, proc_stat, cstr!("self"), Kind::Symlink)?; + let mut buf = [MaybeUninit::<u8>::uninit(); 20]; + let len = crate::backend::fs::syscalls::readlinkat( + self_symlink.as_fd(), + cstr!(""), + &mut buf, + )?; + let pid: &[u8] = unsafe { core::mem::transmute(&buf[..len]) }; // Open "/proc/self". Use our pid to compute the name rather than // literally using "self", as "self" is a symlink. - let proc_self = proc_opendirat(proc, DecInt::new(pid.as_raw_nonzero().get()))?; + let proc_self = proc_opendirat(proc, pid)?; let proc_self_stat = check_proc_entry(Kind::Pid, proc_self.as_fd(), Some(proc_stat)) .map_err(|_err| io::Errno::NOTSUP)?; @@ -314,7 +347,6 @@ fn new_static_fd(fd: OwnedFd, stat: Stat) -> (OwnedFd, Stat) { /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html -#[cfg(feature = "alloc")] fn proc_self_fdinfo() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { static PROC_SELF_FDINFO: StaticFd = StaticFd::new(); @@ -344,18 +376,21 @@ fn proc_self_fdinfo() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html -#[cfg(feature = "alloc")] #[inline] #[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] pub fn proc_self_fdinfo_fd<Fd: AsFd>(fd: Fd) -> io::Result<OwnedFd> { _proc_self_fdinfo(fd.as_fd()) } -#[cfg(feature = "alloc")] fn _proc_self_fdinfo(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> { let (proc_self_fdinfo, proc_self_fdinfo_stat) = proc_self_fdinfo()?; let fd_str = DecInt::from_fd(fd); - open_and_check_file(proc_self_fdinfo, proc_self_fdinfo_stat, fd_str.as_c_str()) + open_and_check_file( + proc_self_fdinfo, + proc_self_fdinfo_stat, + fd_str.as_c_str(), + Kind::File, + ) } /// Returns a handle to a Linux `/proc/self/pagemap` file. @@ -369,7 +404,6 @@ fn _proc_self_fdinfo(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> { /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html /// [Linux pagemap]: https://www.kernel.org/doc/Documentation/vm/pagemap.txt -#[cfg(feature = "alloc")] #[inline] #[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] pub fn proc_self_pagemap() -> io::Result<OwnedFd> { @@ -385,7 +419,6 @@ pub fn proc_self_pagemap() -> io::Result<OwnedFd> { /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html -#[cfg(feature = "alloc")] #[inline] #[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] pub fn proc_self_maps() -> io::Result<OwnedFd> { @@ -401,7 +434,6 @@ pub fn proc_self_maps() -> io::Result<OwnedFd> { /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html -#[cfg(feature = "alloc")] #[inline] #[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] pub fn proc_self_status() -> io::Result<OwnedFd> { @@ -409,15 +441,18 @@ pub fn proc_self_status() -> io::Result<OwnedFd> { } /// Open a file under `/proc/self`. -#[cfg(feature = "alloc")] fn proc_self_file(name: &CStr) -> io::Result<OwnedFd> { let (proc_self, proc_self_stat) = proc_self()?; - open_and_check_file(proc_self, proc_self_stat, name) + open_and_check_file(proc_self, proc_self_stat, name, Kind::File) } /// Open a procfs file within in `dir` and check it for bind mounts. -#[cfg(feature = "alloc")] -fn open_and_check_file(dir: BorrowedFd<'_>, dir_stat: &Stat, name: &CStr) -> io::Result<OwnedFd> { +fn open_and_check_file( + dir: BorrowedFd<'_>, + dir_stat: &Stat, + name: &CStr, + kind: Kind, +) -> io::Result<OwnedFd> { let (_, proc_stat) = proc()?; // Don't use `NOATIME`, because it [requires us to own the file], and when @@ -426,7 +461,11 @@ fn open_and_check_file(dir: BorrowedFd<'_>, dir_stat: &Stat, name: &CStr) -> io: // // [requires us to own the file]: https://man7.org/linux/man-pages/man2/openat.2.html // [to root:root]: https://man7.org/linux/man-pages/man5/proc.5.html - let oflags = OFlags::RDONLY | OFlags::CLOEXEC | OFlags::NOFOLLOW | OFlags::NOCTTY; + let mut oflags = OFlags::RDONLY | OFlags::CLOEXEC | OFlags::NOFOLLOW | OFlags::NOCTTY; + if let Kind::Symlink = kind { + // Open symlinks with `O_PATH`. + oflags |= OFlags::PATH; + } let file = openat(dir, name, oflags, Mode::empty()).map_err(|_err| io::Errno::NOTSUP)?; let file_stat = fstat(&file)?; @@ -436,32 +475,29 @@ fn open_and_check_file(dir: BorrowedFd<'_>, dir_stat: &Stat, name: &CStr) -> io: // we just opened. If we can't find it, there could be a file bind mount on // top of the file we want. // - // As we scan, we also check for ".", to make sure it's the same directory - // as our original directory, to detect mount points, since - // `Dir::read_from` reopens ".". - // // TODO: With Linux 5.8 we might be able to use `statx` and // `STATX_ATTR_MOUNT_ROOT` to detect mountpoints directly instead of doing // this scanning. - let dir = Dir::read_from(dir).map_err(|_err| io::Errno::NOTSUP)?; - // Confirm that we got the same inode. - let dot_stat = dir.stat().map_err(|_err| io::Errno::NOTSUP)?; - if (dot_stat.st_dev, dot_stat.st_ino) != (dir_stat.st_dev, dir_stat.st_ino) { - return Err(io::Errno::NOTSUP); - } + let expected_type = match kind { + Kind::File => FileType::RegularFile, + Kind::Symlink => FileType::Symlink, + _ => unreachable!(), + }; let mut found_file = false; let mut found_dot = false; - for entry in dir { + + let mut buf = [MaybeUninit::uninit(); 2048]; + let mut iter = RawDir::new(dir, &mut buf); + while let Some(entry) = iter.next() { let entry = entry.map_err(|_err| io::Errno::NOTSUP)?; if entry.ino() == file_stat.st_ino - && entry.file_type() == FileType::RegularFile + && entry.file_type() == expected_type && entry.file_name() == name { // We found the file. Proceed to check the file handle. - let _ = - check_proc_entry_with_stat(Kind::File, file.as_fd(), file_stat, Some(proc_stat))?; + let _ = check_proc_entry_with_stat(kind, file.as_fd(), file_stat, Some(proc_stat))?; found_file = true; } else if entry.ino() == dir_stat.st_ino |