Skip to content

Commit 08182e4

Browse files
committed
Auto merge of #93110 - pietroalbini:pa-cve-2022-21658-stable, r=pietroalbini
[stable] Fix CVE 2022 21658 and prepare 1.58.1 Followup to #93071. Includes the fix for CVE-2022-21658. r? `@ghost` cc `@rust-lang/release` `@rust-lang/security`
2 parents 02072b4 + 1c63ec4 commit 08182e4

File tree

27 files changed

+949
-95
lines changed

27 files changed

+949
-95
lines changed

RELEASES.md

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
Version 1.58.1 (2022-01-19)
2+
===========================
3+
4+
* Fix race condition in `std::fs::remove_dir_all` ([CVE-2022-21658])
5+
* [Handle captured arguments in the `useless_format` Clippy lint][clippy/8295]
6+
* [Move `non_send_fields_in_send_ty` Clippy lint to nursery][clippy/8075]
7+
* [Fix wrong error message displayed when some imports are missing][91254]
8+
* [Fix rustfmt not formatting generated files from stdin][92912]
9+
10+
[CVE-2022-21658]: https://www.cve.org/CVERecord?id=CVE-2022-21658]
11+
[91254]: https://github.com/rust-lang/rust/pull/91254
12+
[92912]: https://github.com/rust-lang/rust/pull/92912
13+
[clippy/8075]: https://github.com/rust-lang/rust-clippy/pull/8075
14+
[clippy/8295]: https://github.com/rust-lang/rust-clippy/pull/8295
15+
116
Version 1.58.0 (2022-01-13)
217
==========================
318

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
536536
// This helps us avoid overflow: see issue #72839
537537
// Since compilation is already guaranteed to fail, this is just
538538
// to try to show the 'nicest' possible errors to the user.
539-
if obligation.references_error() {
539+
// We don't check for errors in the `ParamEnv` - in practice,
540+
// it seems to cause us to be overly aggressive in deciding
541+
// to give up searching for candidates, leading to spurious errors.
542+
if obligation.predicate.references_error() {
540543
return;
541544
}
542545

library/std/src/fs.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -2045,13 +2045,17 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
20452045
///
20462046
/// # Platform-specific behavior
20472047
///
2048-
/// This function currently corresponds to `opendir`, `lstat`, `rm` and `rmdir` functions on Unix
2049-
/// and the `FindFirstFile`, `GetFileAttributesEx`, `DeleteFile`, and `RemoveDirectory` functions
2050-
/// on Windows.
2051-
/// Note that, this [may change in the future][changes].
2048+
/// This function currently corresponds to `openat`, `fdopendir`, `unlinkat` and `lstat` functions
2049+
/// on Unix (except for macOS before version 10.10 and REDOX) and the `CreateFileW`,
2050+
/// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtOpenFile` functions on
2051+
/// Windows. Note that, this [may change in the future][changes].
20522052
///
20532053
/// [changes]: io#platform-specific-behavior
20542054
///
2055+
/// On macOS before version 10.10 and REDOX this function is not protected against time-of-check to
2056+
/// time-of-use (TOCTOU) race conditions, and should not be used in security-sensitive code on
2057+
/// those platforms. All other platforms are protected.
2058+
///
20552059
/// # Errors
20562060
///
20572061
/// See [`fs::remove_file`] and [`fs::remove_dir`].

library/std/src/fs/tests.rs

+70
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ use crate::fs::{self, File, OpenOptions};
44
use crate::io::{ErrorKind, SeekFrom};
55
use crate::path::Path;
66
use crate::str;
7+
use crate::sync::Arc;
78
use crate::sys_common::io::test::{tmpdir, TempDir};
89
use crate::thread;
10+
use crate::time::{Duration, Instant};
911

1012
use rand::{rngs::StdRng, RngCore, SeedableRng};
1113

@@ -602,6 +604,21 @@ fn recursive_rmdir_of_symlink() {
602604
assert!(canary.exists());
603605
}
604606

607+
#[test]
608+
fn recursive_rmdir_of_file_fails() {
609+
// test we do not delete a directly specified file.
610+
let tmpdir = tmpdir();
611+
let canary = tmpdir.join("do_not_delete");
612+
check!(check!(File::create(&canary)).write(b"foo"));
613+
let result = fs::remove_dir_all(&canary);
614+
#[cfg(unix)]
615+
error!(result, "Not a directory");
616+
#[cfg(windows)]
617+
error!(result, 267); // ERROR_DIRECTORY - The directory name is invalid.
618+
assert!(result.is_err());
619+
assert!(canary.exists());
620+
}
621+
605622
#[test]
606623
// only Windows makes a distinction between file and directory symlinks.
607624
#[cfg(windows)]
@@ -621,6 +638,59 @@ fn recursive_rmdir_of_file_symlink() {
621638
}
622639
}
623640

641+
#[test]
642+
#[ignore] // takes too much time
643+
fn recursive_rmdir_toctou() {
644+
// Test for time-of-check to time-of-use issues.
645+
//
646+
// Scenario:
647+
// The attacker wants to get directory contents deleted, to which he does not have access.
648+
// He has a way to get a privileged Rust binary call `std::fs::remove_dir_all()` on a
649+
// directory he controls, e.g. in his home directory.
650+
//
651+
// The POC sets up the `attack_dest/attack_file` which the attacker wants to have deleted.
652+
// The attacker repeatedly creates a directory and replaces it with a symlink from
653+
// `victim_del` to `attack_dest` while the victim code calls `std::fs::remove_dir_all()`
654+
// on `victim_del`. After a few seconds the attack has succeeded and
655+
// `attack_dest/attack_file` is deleted.
656+
let tmpdir = tmpdir();
657+
let victim_del_path = tmpdir.join("victim_del");
658+
let victim_del_path_clone = victim_del_path.clone();
659+
660+
// setup dest
661+
let attack_dest_dir = tmpdir.join("attack_dest");
662+
let attack_dest_dir = attack_dest_dir.as_path();
663+
fs::create_dir(attack_dest_dir).unwrap();
664+
let attack_dest_file = tmpdir.join("attack_dest/attack_file");
665+
File::create(&attack_dest_file).unwrap();
666+
667+
let drop_canary_arc = Arc::new(());
668+
let drop_canary_weak = Arc::downgrade(&drop_canary_arc);
669+
670+
eprintln!("x: {:?}", &victim_del_path);
671+
672+
// victim just continuously removes `victim_del`
673+
thread::spawn(move || {
674+
while drop_canary_weak.upgrade().is_some() {
675+
let _ = fs::remove_dir_all(&victim_del_path_clone);
676+
}
677+
});
678+
679+
// attacker (could of course be in a separate process)
680+
let start_time = Instant::now();
681+
while Instant::now().duration_since(start_time) < Duration::from_secs(1000) {
682+
if !attack_dest_file.exists() {
683+
panic!(
684+
"Victim deleted symlinked file outside of victim_del. Attack succeeded in {:?}.",
685+
Instant::now().duration_since(start_time)
686+
);
687+
}
688+
let _ = fs::create_dir(&victim_del_path);
689+
let _ = fs::remove_dir(&victim_del_path);
690+
let _ = symlink_dir(attack_dest_dir, &victim_del_path);
691+
}
692+
}
693+
624694
#[test]
625695
fn unicode_path_is_dir() {
626696
assert!(Path::new(".").is_dir());

0 commit comments

Comments
 (0)