summaryrefslogtreecommitdiffstats
path: root/vendor/rustix-0.36.5/src/io/procfs.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
commit9835e2ae736235810b4ea1c162ca5e65c547e770 (patch)
tree3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/rustix-0.36.5/src/io/procfs.rs
parentReleasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff)
downloadrustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz
rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/rustix-0.36.5/src/io/procfs.rs')
-rw-r--r--vendor/rustix-0.36.5/src/io/procfs.rs464
1 files changed, 0 insertions, 464 deletions
diff --git a/vendor/rustix-0.36.5/src/io/procfs.rs b/vendor/rustix-0.36.5/src/io/procfs.rs
deleted file mode 100644
index c6eeb08e0..000000000
--- a/vendor/rustix-0.36.5/src/io/procfs.rs
+++ /dev/null
@@ -1,464 +0,0 @@
-//! Utilities for working with `/proc`, where Linux's `procfs` is typically
-//! mounted. `/proc` serves as an adjunct to Linux's main syscall surface area,
-//! providing additional features with an awkward interface.
-//!
-//! This module does a considerable amount of work to determine whether `/proc`
-//! is mounted, with actual `procfs`, and without any additional mount points
-//! on top of the paths we open.
-//!
-//! Why all the effort to detect bind mount points? People are doing all kinds
-//! of things with Linux containers these days, with many different privilege
-//! schemes, and we want to avoid making any unnecessary assumptions. Rustix
-//! and its users will sometimes use procfs *implicitly* (when Linux gives them
-//! no better options), in ways that aren't obvious from their public APIs.
-//! These filesystem accesses might not be visible to someone auditing the main
-//! code of an application for places which may be influenced by the filesystem
-//! namespace. So with the checking here, they may fail, but they won't be able
-//! to succeed with bogus results.
-
-use crate::fd::{AsFd, BorrowedFd, OwnedFd};
-use crate::ffi::CStr;
-use crate::fs::{
- cwd, fstat, fstatfs, major, openat, renameat, Dir, FileType, Mode, OFlags, Stat,
- PROC_SUPER_MAGIC,
-};
-use crate::io;
-use crate::path::DecInt;
-use crate::process::getpid;
-#[cfg(feature = "rustc-dep-of-std")]
-use core::lazy::OnceCell;
-#[cfg(not(feature = "rustc-dep-of-std"))]
-use once_cell::sync::OnceCell;
-
-/// Linux's procfs always uses inode 1 for its root directory.
-const PROC_ROOT_INO: u64 = 1;
-
-// Identify an entry within "/proc", to determine which anomalies to check for.
-#[derive(Copy, Clone, Debug)]
-enum Kind {
- Proc,
- Pid,
- Fd,
- File,
-}
-
-/// Check a subdirectory of "/proc" for anomalies.
-fn check_proc_entry(
- kind: Kind,
- entry: BorrowedFd<'_>,
- proc_stat: Option<&Stat>,
-) -> io::Result<Stat> {
- let entry_stat = fstat(entry)?;
- check_proc_entry_with_stat(kind, entry, entry_stat, proc_stat)
-}
-
-/// Check a subdirectory of "/proc" for anomalies, using the provided `Stat`.
-fn check_proc_entry_with_stat(
- kind: Kind,
- entry: BorrowedFd<'_>,
- entry_stat: Stat,
- proc_stat: Option<&Stat>,
-) -> io::Result<Stat> {
- // Check the filesystem magic.
- check_procfs(entry)?;
-
- match kind {
- Kind::Proc => check_proc_root(entry, &entry_stat)?,
- Kind::Pid | Kind::Fd => check_proc_subdir(entry, &entry_stat, proc_stat)?,
- Kind::File => check_proc_file(&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::Fd => {
- // Check that the "/proc/self/fd" directory doesn't have any extraneous
- // links into it (which might include unexpected subdirectories).
- if entry_stat.st_nlink != 2 {
- return Err(io::Errno::NOTSUP);
- }
- }
- Kind::Pid | Kind::Proc => {
- // Check that the "/proc" and "/proc/self" directories aren't empty.
- if entry_stat.st_nlink <= 2 {
- return Err(io::Errno::NOTSUP);
- }
- }
- Kind::File => {
- // Check that files 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)
-}
-
-fn check_proc_root(entry: BorrowedFd<'_>, stat: &Stat) -> io::Result<()> {
- // We use `O_DIRECTORY` for proc directories, so open should fail if we
- // don't get a directory when we expect one.
- assert_eq!(FileType::from_raw_mode(stat.st_mode), FileType::Directory);
-
- // Check the root inode number.
- if stat.st_ino != PROC_ROOT_INO {
- return Err(io::Errno::NOTSUP);
- }
-
- // Proc is a non-device filesystem, so check for major number 0.
- // <https://www.kernel.org/doc/Documentation/admin-guide/devices.txt>
- if major(stat.st_dev) != 0 {
- return Err(io::Errno::NOTSUP);
- }
-
- // Check that "/proc" is a mountpoint.
- if !is_mountpoint(entry) {
- return Err(io::Errno::NOTSUP);
- }
-
- Ok(())
-}
-
-fn check_proc_subdir(
- entry: BorrowedFd<'_>,
- stat: &Stat,
- proc_stat: Option<&Stat>,
-) -> io::Result<()> {
- // We use `O_DIRECTORY` for proc directories, so open should fail if we
- // don't get a directory when we expect one.
- assert_eq!(FileType::from_raw_mode(stat.st_mode), FileType::Directory);
-
- check_proc_nonroot(stat, proc_stat)?;
-
- // Check that subdirectories of "/proc" are not mount points.
- if is_mountpoint(entry) {
- return Err(io::Errno::NOTSUP);
- }
-
- Ok(())
-}
-
-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 {
- 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 {
- return Err(io::Errno::NOTSUP);
- }
-
- // Check that we're still in procfs.
- if stat.st_dev != proc_stat.unwrap().st_dev {
- return Err(io::Errno::NOTSUP);
- }
-
- Ok(())
-}
-
-/// Check that `file` is opened on a `procfs` filesystem.
-fn check_procfs(file: BorrowedFd<'_>) -> io::Result<()> {
- let statfs = fstatfs(file)?;
- let f_type = statfs.f_type;
- if f_type != PROC_SUPER_MAGIC {
- return Err(io::Errno::NOTSUP);
- }
-
- Ok(())
-}
-
-/// Check whether the given directory handle is a mount point. We use a
-/// `renameat` call that would otherwise fail, but which fails with `EXDEV`
-/// first if it would cross a mount point.
-fn is_mountpoint(file: BorrowedFd<'_>) -> bool {
- let err = renameat(file, cstr!("../."), file, cstr!(".")).unwrap_err();
- match err {
- io::Errno::XDEV => true, // the rename failed due to crossing a mount point
- io::Errno::BUSY => false, // the rename failed normally
- _ => panic!("Unexpected error from `renameat`: {:?}", err),
- }
-}
-
-/// Open a directory in `/proc`, mapping all errors to `io::Errno::NOTSUP`.
-fn proc_opendirat<P: crate::path::Arg, Fd: AsFd>(dirfd: Fd, path: P) -> io::Result<OwnedFd> {
- // We could add `PATH`|`NOATIME` here but Linux 2.6.32 doesn't support it.
- // Also for `NOATIME` see the comment in `open_and_check_file`.
- let oflags = OFlags::NOFOLLOW | OFlags::DIRECTORY | OFlags::CLOEXEC | OFlags::NOCTTY;
- openat(dirfd, path, oflags, Mode::empty()).map_err(|_err| io::Errno::NOTSUP)
-}
-
-/// Returns a handle to Linux's `/proc` directory.
-///
-/// This ensures that `/proc` is procfs, that nothing is mounted on top of it,
-/// and that it looks normal. It also returns the `Stat` of `/proc`.
-///
-/// # References
-/// - [Linux]
-///
-/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
-fn proc() -> io::Result<(BorrowedFd<'static>, &'static Stat)> {
- static PROC: StaticFd = StaticFd::new();
-
- // `OnceBox` is "racey" in that the initialization function may run
- // multiple times. We're ok with that, since the initialization function
- // has no side effects.
- PROC.get_or_try_init(|| {
- // Open "/proc".
- let proc = proc_opendirat(cwd(), cstr!("/proc"))?;
- let proc_stat =
- check_proc_entry(Kind::Proc, proc.as_fd(), None).map_err(|_err| io::Errno::NOTSUP)?;
-
- Ok(new_static_fd(proc, proc_stat))
- })
- .map(|(fd, stat)| (fd.as_fd(), stat))
-}
-
-/// Returns a handle to Linux's `/proc/self` directory.
-///
-/// This ensures that `/proc/self` is procfs, that nothing is mounted on top of
-/// it, and that it looks normal. It also returns the `Stat` of `/proc/self`.
-///
-/// # References
-/// - [Linux]
-///
-/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
-fn proc_self() -> io::Result<(BorrowedFd<'static>, &'static Stat)> {
- static PROC_SELF: StaticFd = StaticFd::new();
-
- // The init function here may run multiple times; see above.
- PROC_SELF
- .get_or_try_init(|| {
- let (proc, proc_stat) = proc()?;
-
- let pid = getpid();
-
- // 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_stat = check_proc_entry(Kind::Pid, proc_self.as_fd(), Some(proc_stat))
- .map_err(|_err| io::Errno::NOTSUP)?;
-
- Ok(new_static_fd(proc_self, proc_self_stat))
- })
- .map(|(owned, stat)| (owned.as_fd(), stat))
-}
-
-/// Returns a handle to Linux's `/proc/self/fd` directory.
-///
-/// This ensures that `/proc/self/fd` is `procfs`, that nothing is mounted on
-/// top of it, and that it looks normal.
-///
-/// # References
-/// - [Linux]
-///
-/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
-#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))]
-pub fn proc_self_fd() -> io::Result<BorrowedFd<'static>> {
- static PROC_SELF_FD: StaticFd = StaticFd::new();
-
- // The init function here may run multiple times; see above.
- PROC_SELF_FD
- .get_or_try_init(|| {
- let (_, proc_stat) = proc()?;
-
- let (proc_self, _proc_self_stat) = proc_self()?;
-
- // Open "/proc/self/fd".
- let proc_self_fd = proc_opendirat(proc_self, cstr!("fd"))?;
- let proc_self_fd_stat =
- check_proc_entry(Kind::Fd, proc_self_fd.as_fd(), Some(proc_stat))
- .map_err(|_err| io::Errno::NOTSUP)?;
-
- Ok(new_static_fd(proc_self_fd, proc_self_fd_stat))
- })
- .map(|(owned, _stat)| owned.as_fd())
-}
-
-type StaticFd = OnceCell<(OwnedFd, Stat)>;
-
-#[inline]
-fn new_static_fd(fd: OwnedFd, stat: Stat) -> (OwnedFd, Stat) {
- (fd, stat)
-}
-
-/// Returns a handle to Linux's `/proc/self/fdinfo` directory.
-///
-/// This ensures that `/proc/self/fdinfo` is `procfs`, that nothing is mounted
-/// on top of it, and that it looks normal. It also returns the `Stat` of
-/// `/proc/self/fd`.
-///
-/// # References
-/// - [Linux]
-///
-/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
-fn proc_self_fdinfo() -> io::Result<(BorrowedFd<'static>, &'static Stat)> {
- static PROC_SELF_FDINFO: StaticFd = StaticFd::new();
-
- PROC_SELF_FDINFO
- .get_or_try_init(|| {
- let (_, proc_stat) = proc()?;
-
- let (proc_self, _proc_self_stat) = proc_self()?;
-
- // Open "/proc/self/fdinfo".
- let proc_self_fdinfo = proc_opendirat(proc_self, cstr!("fdinfo"))?;
- let proc_self_fdinfo_stat =
- check_proc_entry(Kind::Fd, proc_self_fdinfo.as_fd(), Some(proc_stat))
- .map_err(|_err| io::Errno::NOTSUP)?;
-
- Ok((proc_self_fdinfo, proc_self_fdinfo_stat))
- })
- .map(|(owned, stat)| (owned.as_fd(), stat))
-}
-
-/// Returns a handle to a Linux `/proc/self/fdinfo/<fd>` file.
-///
-/// This ensures that `/proc/self/fdinfo/<fd>` is `procfs`, that nothing is
-/// mounted on top of it, and that it looks normal.
-///
-/// # References
-/// - [Linux]
-///
-/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
-#[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())
-}
-
-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())
-}
-
-/// Returns a handle to a Linux `/proc/self/pagemap` file.
-///
-/// This ensures that `/proc/self/pagemap` is `procfs`, that nothing is
-/// mounted on top of it, and that it looks normal.
-///
-/// # References
-/// - [Linux]
-/// - [Linux pagemap]
-///
-/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
-/// [Linux pagemap]: https://www.kernel.org/doc/Documentation/vm/pagemap.txt
-#[inline]
-#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))]
-pub fn proc_self_pagemap() -> io::Result<OwnedFd> {
- proc_self_file(cstr!("pagemap"))
-}
-
-/// Returns a handle to a Linux `/proc/self/maps` file.
-///
-/// This ensures that `/proc/self/maps` is `procfs`, that nothing is
-/// mounted on top of it, and that it looks normal.
-///
-/// # References
-/// - [Linux]
-///
-/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
-#[inline]
-#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))]
-pub fn proc_self_maps() -> io::Result<OwnedFd> {
- proc_self_file(cstr!("maps"))
-}
-
-/// Returns a handle to a Linux `/proc/self/status` file.
-///
-/// This ensures that `/proc/self/status` is `procfs`, that nothing is
-/// mounted on top of it, and that it looks normal.
-///
-/// # References
-/// - [Linux]
-///
-/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
-#[inline]
-#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))]
-pub fn proc_self_status() -> io::Result<OwnedFd> {
- proc_self_file(cstr!("status"))
-}
-
-/// Open a file under `/proc/self`.
-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 a procfs file within in `dir` and check it for bind mounts.
-fn open_and_check_file(dir: BorrowedFd, dir_stat: &Stat, name: &CStr) -> io::Result<OwnedFd> {
- let (_, proc_stat) = proc()?;
-
- // Don't use `NOATIME`, because it [requires us to own the file], and when
- // a process sets itself non-dumpable Linux changes the user:group of its
- // `/proc/<pid>` files [to root:root].
- //
- // [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 file = openat(dir, name, oflags, Mode::empty()).map_err(|_err| io::Errno::NOTSUP)?;
- let file_stat = fstat(&file)?;
-
- // `is_mountpoint` only works on directory mount points, not file mount
- // points. To detect file mount points, scan the parent directory to see
- // if we can find a regular file with an inode and name that matches the
- // file 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 mut found_file = false;
- let mut found_dot = false;
- for entry in dir {
- let entry = entry.map_err(|_err| io::Errno::NOTSUP)?;
- if entry.ino() == file_stat.st_ino
- && entry.file_type() == FileType::RegularFile
- && 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))?;
-
- found_file = true;
- } else if entry.ino() == dir_stat.st_ino
- && entry.file_type() == FileType::Directory
- && entry.file_name() == cstr!(".")
- {
- // We found ".", and it's the right ".".
- found_dot = true;
- }
- }
-
- if found_file && found_dot {
- Ok(file)
- } else {
- Err(io::Errno::NOTSUP)
- }
-}