From 0cc154c2a9ab08568a232f0c67b756c97976332d Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 11 Apr 2024 12:09:07 -0400 Subject: [PATCH] Avoid TOCTOU errors in cache initialization (#10884) ## Summary I believe this should close https://github.com/astral-sh/ruff/issues/10880? The `.gitignore` creation seems ok, since it truncates, but using `cachedir::is_tagged` followed by `cachedir::add_tag` is not safe, as `cachedir::add_tag` _fails_ if the file already exists. This also matches the structure of the code in `uv`. Closes https://github.com/astral-sh/ruff/issues/10880. --- crates/ruff/src/cache.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/ruff/src/cache.rs b/crates/ruff/src/cache.rs index dddd8cb64a..001969a267 100644 --- a/crates/ruff/src/cache.rs +++ b/crates/ruff/src/cache.rs @@ -375,15 +375,17 @@ pub(crate) fn init(path: &Path) -> Result<()> { fs::create_dir_all(path.join(VERSION))?; // Add the CACHEDIR.TAG. - if !cachedir::is_tagged(path)? { - cachedir::add_tag(path)?; - } + cachedir::ensure_tag(path)?; // Add the .gitignore. - let gitignore_path = path.join(".gitignore"); - if !gitignore_path.exists() { - let mut file = fs::File::create(gitignore_path)?; - file.write_all(b"# Automatically created by ruff.\n*\n")?; + match fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(path.join(".gitignore")) + { + Ok(mut file) => file.write_all(b"# Automatically created by ruff.\n*\n")?, + Err(err) if err.kind() == io::ErrorKind::AlreadyExists => (), + Err(err) => return Err(err.into()), } Ok(())