4
4
//! filesystem. All methods in this module represent cross-platform filesystem
5
5
//! operations. Extra platform-specific functionality can be found in the
6
6
//! 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.
7
28
8
29
#![ stable( feature = "rust1" , since = "1.0.0" ) ]
9
30
#![ deny( unsafe_op_in_unsafe_fn) ]
@@ -548,13 +569,14 @@ impl File {
548
569
/// non-exhaustive list of likely errors.
549
570
///
550
571
/// This option is useful because it is atomic. Otherwise between checking whether a file
551
- /// exists and creating a new one, the file may have been created by another process (a TOCTOU
572
+ /// exists and creating a new one, the file may have been created by another process (a [ TOCTOU]
552
573
/// race condition / attack).
553
574
///
554
575
/// This can also be written using
555
576
/// `File::options().read(true).write(true).create_new(true).open(...)`.
556
577
///
557
578
/// [`AlreadyExists`]: crate::io::ErrorKind::AlreadyExists
579
+ /// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
558
580
///
559
581
/// # Examples
560
582
///
@@ -1610,7 +1632,7 @@ impl OpenOptions {
1610
1632
///
1611
1633
/// This option is useful because it is atomic. Otherwise between checking
1612
1634
/// whether a file exists and creating a new one, the file may have been
1613
- /// created by another process (a TOCTOU race condition / attack).
1635
+ /// created by another process (a [ TOCTOU] race condition / attack).
1614
1636
///
1615
1637
/// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are
1616
1638
/// ignored.
@@ -1621,6 +1643,7 @@ impl OpenOptions {
1621
1643
/// [`.create()`]: OpenOptions::create
1622
1644
/// [`.truncate()`]: OpenOptions::truncate
1623
1645
/// [`AlreadyExists`]: io::ErrorKind::AlreadyExists
1646
+ /// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
1624
1647
///
1625
1648
/// # Examples
1626
1649
///
@@ -2954,17 +2977,17 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
2954
2977
/// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtCreateFile`.
2955
2978
///
2956
2979
/// ## Time-of-check to time-of-use (TOCTOU) race conditions
2957
- /// On a few platforms there is no way to remove a directory's contents without following symlinks
2958
- /// unless you perform a check and then operate on paths based on that directory.
2959
- /// This allows concurrently-running code to replace the directory with a symlink after the check,
2960
- /// causing a removal to instead operate on a path based on the symlink. This is a TOCTOU race.
2961
- /// By default, `fs::remove_dir_all` protects against a symlink TOCTOU race on all platforms
2962
- /// except the following. It should not be used in security-sensitive contexts on these platforms:
2963
- /// - Miri: Even when emulating targets where the underlying implementation will protect against
2964
- /// TOCTOU races, Miri will not do so.
2965
- /// - Redox OS: This function does not protect against TOCTOU races, as Redox does not implement
2966
- /// the required platform support to do so.
2980
+ /// See the [module-level TOCTOU explanation](self#time-of-check-to-time-of-use-toctou).
2981
+ ///
2982
+ /// On most platforms, `fs::remove_dir_all` protects against symlink TOCTOU races by default.
2983
+ /// However, on the following platforms, this protection is not provided and the function should
2984
+ /// not be used in security-sensitive contexts:
2985
+ /// - **Miri**: Even when emulating targets where the underlying implementation will protect against
2986
+ /// TOCTOU races, Miri will not do so.
2987
+ /// - **Redox OS**: This function does not protect against TOCTOU races, as Redox does not implement
2988
+ /// the required platform support to do so.
2967
2989
///
2990
+ /// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
2968
2991
/// [changes]: io#platform-specific-behavior
2969
2992
///
2970
2993
/// # Errors
@@ -3238,7 +3261,7 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder {
3238
3261
/// permission is denied on one of the parent directories.
3239
3262
///
3240
3263
/// Note that while this avoids some pitfalls of the `exists()` method, it still can not
3241
- /// prevent time-of-check to time-of-use (TOCTOU) bugs. You should only use it in scenarios
3264
+ /// prevent time-of-check to time-of-use ([ TOCTOU] ) bugs. You should only use it in scenarios
3242
3265
/// where those bugs are not an issue.
3243
3266
///
3244
3267
/// # Examples
@@ -3251,6 +3274,7 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder {
3251
3274
/// ```
3252
3275
///
3253
3276
/// [`Path::exists`]: crate::path::Path::exists
3277
+ /// [TOCTOU]: self#time-of-check-to-time-of-use-toctou
3254
3278
#[ stable( feature = "fs_try_exists" , since = "1.81.0" ) ]
3255
3279
#[ inline]
3256
3280
pub fn exists < P : AsRef < Path > > ( path : P ) -> io:: Result < bool > {
0 commit comments