diff --git a/services/network/public/cpp/simple_url_loader.cc b/services/network/public/cpp/simple_url_loader.cc index 45185b057d4f9..2a83a617e1c47 100644 --- a/services/network/public/cpp/simple_url_loader.cc +++ b/services/network/public/cpp/simple_url_loader.cc @@ -1020,16 +1020,24 @@ class SaveToFileBodyHandler : public BodyHandler { DCHECK(!file_.IsValid()); DCHECK(!body_reader_); - bool have_path = !create_temp_file_; - if (!have_path) { - DCHECK(create_temp_file_); - have_path = base::CreateTemporaryFile(&path_); - // CreateTemporaryFile() creates an empty file. - if (have_path) + if (create_temp_file_) { + // Use CreateAndOpenTemporaryFileInDir() so that the file descriptor + // returned by mkstemp() is the one we keep writing to. The previous + // implementation called base::CreateTemporaryFile() (which closes the + // mkstemp fd) and then re-opened the path with FLAG_CREATE_ALWAYS + // (O_CREAT|O_TRUNC, no O_NOFOLLOW). On macOS the network-service + // sandbox has file-write* on the same _CS_DARWIN_USER_TEMP_DIR, so a + // compromised network service could swap the temp file for a symlink + // between those two opens and have the client process (the browser) + // write attacker-chosen body bytes through the symlink. + base::FilePath temp_dir; + if (base::GetTempDir(&temp_dir)) { + file_ = base::CreateAndOpenTemporaryFileInDir(temp_dir, &path_); + } + if (file_.IsValid()) { owns_file_ = true; - } - - if (have_path) { + } + } else { // Try to initialize |file_|, creating the file if needed. file_.Initialize( path_, base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS);