Skip to content

Commit e95751a

Browse files
committed
Explain TOCTOU on the top of std::fs, and ref it in functions
Signed-off-by: xizheyin <[email protected]> Signed-off-by: xizheyin <[email protected]>
1 parent 91fad92 commit e95751a

File tree

2 files changed

+41
-15
lines changed

2 files changed

+41
-15
lines changed

library/std/src/fs.rs

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,27 @@
44
//! filesystem. All methods in this module represent cross-platform filesystem
55
//! operations. Extra platform-specific functionality can be found in the
66
//! extension traits of `std::os::$platform`.
7+
//!
8+
//! # Time of Check to Time of Use (TOCTOU)
9+
//!
10+
//! Many filesystem operations are subject to a race condition known as "Time of Check to Time of Use"
11+
//! (TOCTOU). This occurs when a program checks a condition (like file existence or permissions)
12+
//! and then uses the result of that check to make a decision, but the condition may have changed
13+
//! between the check and the use.
14+
//!
15+
//! For example, checking if a file exists and then creating it if it doesn't is vulnerable to
16+
//! TOCTOU - another process could create the file between your check and creation attempt.
17+
//!
18+
//! Another example is with symbolic links: when removing a directory, if another process replaces
19+
//! the directory with a symbolic link between the check and the removal operation, the removal
20+
//! might affect the wrong location. This is why operations like [`remove_dir_all`] need to use
21+
//! atomic operations to prevent such race conditions.
22+
//!
23+
//! To avoid TOCTOU issues:
24+
//! - Be aware that metadata operations (like [`metadata`] or [`symlink_metadata`]) may be affected by
25+
//! changes made by other processes.
26+
//! - Use atomic operations when possible (like [`File::create_new`] instead of checking existence then creating).
27+
//! - Keep file open for the duration of operations.
728
829
#![stable(feature = "rust1", since = "1.0.0")]
930
#![deny(unsafe_op_in_unsafe_fn)]
@@ -549,13 +570,14 @@ impl File {
549570
/// non-exhaustive list of likely errors.
550571
///
551572
/// This option is useful because it is atomic. Otherwise between checking whether a file
552-
/// exists and creating a new one, the file may have been created by another process (a TOCTOU
573+
/// exists and creating a new one, the file may have been created by another process (a [TOCTOU]
553574
/// race condition / attack).
554575
///
555576
/// This can also be written using
556577
/// `File::options().read(true).write(true).create_new(true).open(...)`.
557578
///
558579
/// [`AlreadyExists`]: crate::io::ErrorKind::AlreadyExists
580+
/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
559581
///
560582
/// # Examples
561583
///
@@ -1580,7 +1602,7 @@ impl OpenOptions {
15801602
///
15811603
/// This option is useful because it is atomic. Otherwise between checking
15821604
/// whether a file exists and creating a new one, the file may have been
1583-
/// created by another process (a TOCTOU race condition / attack).
1605+
/// created by another process (a [TOCTOU] race condition / attack).
15841606
///
15851607
/// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are
15861608
/// ignored.
@@ -1591,6 +1613,7 @@ impl OpenOptions {
15911613
/// [`.create()`]: OpenOptions::create
15921614
/// [`.truncate()`]: OpenOptions::truncate
15931615
/// [`AlreadyExists`]: io::ErrorKind::AlreadyExists
1616+
/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
15941617
///
15951618
/// # Examples
15961619
///
@@ -2924,17 +2947,17 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
29242947
/// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtCreateFile`.
29252948
///
29262949
/// ## Time-of-check to time-of-use (TOCTOU) race conditions
2927-
/// On a few platforms there is no way to remove a directory's contents without following symlinks
2928-
/// unless you perform a check and then operate on paths based on that directory.
2929-
/// This allows concurrently-running code to replace the directory with a symlink after the check,
2930-
/// causing a removal to instead operate on a path based on the symlink. This is a TOCTOU race.
2931-
/// By default, `fs::remove_dir_all` protects against a symlink TOCTOU race on all platforms
2932-
/// except the following. It should not be used in security-sensitive contexts on these platforms:
2933-
/// - Miri: Even when emulating targets where the underlying implementation will protect against
2934-
/// TOCTOU races, Miri will not do so.
2935-
/// - Redox OS: This function does not protect against TOCTOU races, as Redox does not implement
2936-
/// the required platform support to do so.
2950+
/// See the [module-level TOCTOU explanation](self#time-of-check-to-time-of-use-toctou).
2951+
///
2952+
/// On most platforms, `fs::remove_dir_all` protects against symlink TOCTOU races by default.
2953+
/// However, on the following platforms, this protection is not provided and the function should
2954+
/// not be used in security-sensitive contexts:
2955+
/// - **Miri**: Even when emulating targets where the underlying implementation will protect against
2956+
/// TOCTOU races, Miri will not do so.
2957+
/// - **Redox OS**: This function does not protect against TOCTOU races, as Redox does not implement
2958+
/// the required platform support to do so.
29372959
///
2960+
/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
29382961
/// [changes]: io#platform-specific-behavior
29392962
///
29402963
/// # Errors
@@ -3208,7 +3231,7 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder {
32083231
/// permission is denied on one of the parent directories.
32093232
///
32103233
/// Note that while this avoids some pitfalls of the `exists()` method, it still can not
3211-
/// prevent time-of-check to time-of-use (TOCTOU) bugs. You should only use it in scenarios
3234+
/// prevent time-of-check to time-of-use ([TOCTOU]) bugs. You should only use it in scenarios
32123235
/// where those bugs are not an issue.
32133236
///
32143237
/// # Examples
@@ -3221,6 +3244,7 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder {
32213244
/// ```
32223245
///
32233246
/// [`Path::exists`]: crate::path::Path::exists
3247+
/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
32243248
#[stable(feature = "fs_try_exists", since = "1.81.0")]
32253249
#[inline]
32263250
pub fn exists<P: AsRef<Path>>(path: P) -> io::Result<bool> {

library/std/src/path.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3090,7 +3090,7 @@ impl Path {
30903090
/// Returns `true` if the path points at an existing entity.
30913091
///
30923092
/// Warning: this method may be error-prone, consider using [`try_exists()`] instead!
3093-
/// It also has a risk of introducing time-of-check to time-of-use (TOCTOU) bugs.
3093+
/// It also has a risk of introducing time-of-check to time-of-use ([TOCTOU]) bugs.
30943094
///
30953095
/// This function will traverse symbolic links to query information about the
30963096
/// destination file.
@@ -3111,6 +3111,7 @@ impl Path {
31113111
/// check errors, call [`Path::try_exists`].
31123112
///
31133113
/// [`try_exists()`]: Self::try_exists
3114+
/// [TOCTOU]: fs#time-of-check-to-time-of-use-toctou
31143115
#[stable(feature = "path_ext", since = "1.5.0")]
31153116
#[must_use]
31163117
#[inline]
@@ -3130,7 +3131,7 @@ impl Path {
31303131
/// permission is denied on one of the parent directories.
31313132
///
31323133
/// Note that while this avoids some pitfalls of the `exists()` method, it still can not
3133-
/// prevent time-of-check to time-of-use (TOCTOU) bugs. You should only use it in scenarios
3134+
/// prevent time-of-check to time-of-use ([TOCTOU]) bugs. You should only use it in scenarios
31343135
/// where those bugs are not an issue.
31353136
///
31363137
/// This is an alias for [`std::fs::exists`](crate::fs::exists).
@@ -3143,6 +3144,7 @@ impl Path {
31433144
/// assert!(Path::new("/root/secret_file.txt").try_exists().is_err());
31443145
/// ```
31453146
///
3147+
/// [TOCTOU]: fs#time-of-check-to-time-of-use-toctou
31463148
/// [`exists()`]: Self::exists
31473149
#[stable(feature = "path_try_exists", since = "1.63.0")]
31483150
#[inline]

0 commit comments

Comments
 (0)