Explain TOCTOU on the top of std::fs, and ref it in functions

Signed-off-by: xizheyin <xizheyin@smail.nju.edu.cn>

Signed-off-by: xizheyin <xizheyin@smail.nju.edu.cn>
This commit is contained in:
xizheyin 2025-06-01 14:56:45 +08:00
parent 91fad92585
commit e95751a9ff
2 changed files with 41 additions and 15 deletions

View file

@ -4,6 +4,27 @@
//! filesystem. All methods in this module represent cross-platform filesystem
//! operations. Extra platform-specific functionality can be found in the
//! extension traits of `std::os::$platform`.
//!
//! # Time of Check to Time of Use (TOCTOU)
//!
//! Many filesystem operations are subject to a race condition known as "Time of Check to Time of Use"
//! (TOCTOU). This occurs when a program checks a condition (like file existence or permissions)
//! and then uses the result of that check to make a decision, but the condition may have changed
//! between the check and the use.
//!
//! For example, checking if a file exists and then creating it if it doesn't is vulnerable to
//! TOCTOU - another process could create the file between your check and creation attempt.
//!
//! Another example is with symbolic links: when removing a directory, if another process replaces
//! the directory with a symbolic link between the check and the removal operation, the removal
//! might affect the wrong location. This is why operations like [`remove_dir_all`] need to use
//! atomic operations to prevent such race conditions.
//!
//! To avoid TOCTOU issues:
//! - Be aware that metadata operations (like [`metadata`] or [`symlink_metadata`]) may be affected by
//! changes made by other processes.
//! - Use atomic operations when possible (like [`File::create_new`] instead of checking existence then creating).
//! - Keep file open for the duration of operations.
#![stable(feature = "rust1", since = "1.0.0")]
#![deny(unsafe_op_in_unsafe_fn)]
@ -549,13 +570,14 @@ impl File {
/// non-exhaustive list of likely errors.
///
/// This option is useful because it is atomic. Otherwise between checking whether a file
/// exists and creating a new one, the file may have been created by another process (a TOCTOU
/// exists and creating a new one, the file may have been created by another process (a [TOCTOU]
/// race condition / attack).
///
/// This can also be written using
/// `File::options().read(true).write(true).create_new(true).open(...)`.
///
/// [`AlreadyExists`]: crate::io::ErrorKind::AlreadyExists
/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
///
/// # Examples
///
@ -1580,7 +1602,7 @@ impl OpenOptions {
///
/// This option is useful because it is atomic. Otherwise between checking
/// whether a file exists and creating a new one, the file may have been
/// created by another process (a TOCTOU race condition / attack).
/// created by another process (a [TOCTOU] race condition / attack).
///
/// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are
/// ignored.
@ -1591,6 +1613,7 @@ impl OpenOptions {
/// [`.create()`]: OpenOptions::create
/// [`.truncate()`]: OpenOptions::truncate
/// [`AlreadyExists`]: io::ErrorKind::AlreadyExists
/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
///
/// # Examples
///
@ -2924,17 +2947,17 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
/// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtCreateFile`.
///
/// ## Time-of-check to time-of-use (TOCTOU) race conditions
/// On a few platforms there is no way to remove a directory's contents without following symlinks
/// unless you perform a check and then operate on paths based on that directory.
/// This allows concurrently-running code to replace the directory with a symlink after the check,
/// causing a removal to instead operate on a path based on the symlink. This is a TOCTOU race.
/// By default, `fs::remove_dir_all` protects against a symlink TOCTOU race on all platforms
/// except the following. It should not be used in security-sensitive contexts on these platforms:
/// - Miri: Even when emulating targets where the underlying implementation will protect against
/// TOCTOU races, Miri will not do so.
/// - Redox OS: This function does not protect against TOCTOU races, as Redox does not implement
/// the required platform support to do so.
/// See the [module-level TOCTOU explanation](self#time-of-check-to-time-of-use-toctou).
///
/// On most platforms, `fs::remove_dir_all` protects against symlink TOCTOU races by default.
/// However, on the following platforms, this protection is not provided and the function should
/// not be used in security-sensitive contexts:
/// - **Miri**: Even when emulating targets where the underlying implementation will protect against
/// TOCTOU races, Miri will not do so.
/// - **Redox OS**: This function does not protect against TOCTOU races, as Redox does not implement
/// the required platform support to do so.
///
/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
/// [changes]: io#platform-specific-behavior
///
/// # Errors
@ -3208,7 +3231,7 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder {
/// permission is denied on one of the parent directories.
///
/// Note that while this avoids some pitfalls of the `exists()` method, it still can not
/// prevent time-of-check to time-of-use (TOCTOU) bugs. You should only use it in scenarios
/// prevent time-of-check to time-of-use ([TOCTOU]) bugs. You should only use it in scenarios
/// where those bugs are not an issue.
///
/// # Examples
@ -3221,6 +3244,7 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder {
/// ```
///
/// [`Path::exists`]: crate::path::Path::exists
/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
#[stable(feature = "fs_try_exists", since = "1.81.0")]
#[inline]
pub fn exists<P: AsRef<Path>>(path: P) -> io::Result<bool> {

View file

@ -3090,7 +3090,7 @@ impl Path {
/// Returns `true` if the path points at an existing entity.
///
/// Warning: this method may be error-prone, consider using [`try_exists()`] instead!
/// It also has a risk of introducing time-of-check to time-of-use (TOCTOU) bugs.
/// It also has a risk of introducing time-of-check to time-of-use ([TOCTOU]) bugs.
///
/// This function will traverse symbolic links to query information about the
/// destination file.
@ -3111,6 +3111,7 @@ impl Path {
/// check errors, call [`Path::try_exists`].
///
/// [`try_exists()`]: Self::try_exists
/// [TOCTOU]: fs#time-of-check-to-time-of-use-toctou
#[stable(feature = "path_ext", since = "1.5.0")]
#[must_use]
#[inline]
@ -3130,7 +3131,7 @@ impl Path {
/// permission is denied on one of the parent directories.
///
/// Note that while this avoids some pitfalls of the `exists()` method, it still can not
/// prevent time-of-check to time-of-use (TOCTOU) bugs. You should only use it in scenarios
/// prevent time-of-check to time-of-use ([TOCTOU]) bugs. You should only use it in scenarios
/// where those bugs are not an issue.
///
/// This is an alias for [`std::fs::exists`](crate::fs::exists).
@ -3143,6 +3144,7 @@ impl Path {
/// assert!(Path::new("/root/secret_file.txt").try_exists().is_err());
/// ```
///
/// [TOCTOU]: fs#time-of-check-to-time-of-use-toctou
/// [`exists()`]: Self::exists
#[stable(feature = "path_try_exists", since = "1.63.0")]
#[inline]