Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactRace in Network
DescriptionRace in Network
ComponentNetwork
Bug ClassRace
Tracker516979551
Fix commit037c5ddb3336 (chromium/src) +15/-15
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-08

Changed Functions

FunctionChangeNotes
if
services/network/public/cpp/simple_url_loader.cc
modified

Files Changed

  • services/network/public/cpp/simple_url_loader.cc
From 037c5ddb3336e88e3215f9e2cb903282d032b653 Mon Sep 17 00:00:00 2001
From: Kenichi Ishibashi <bashi@chromium.org>
Date: Tue, 02 Jun 2026 20:42:51 -0700
Subject: [PATCH] Improve temporary file creation and error handling in SimpleURLLoader

This CL updates SimpleURLLoader to use
base::CreateAndOpenTemporaryFileInDir() when downloading to a temporary
file. This atomically creates and opens the file, retaining the file
descriptor directly and avoiding the previous pattern of closing and
re-opening the file by path.

Additionally, it improves error reporting on initialization failure by
explicitly mapping base::File::error_details() to a net::Error via
net::FileErrorToNetError().

Bug: 516979551
Change-Id: I353bac993212cbee76cb8a1c3c086e73ff4e6369
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7885361
Reviewed-by: mmenke <mmenke@chromium.org>
Commit-Queue: Kenichi Ishibashi <bashi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1640681}
---

diff --git a/services/network/public/cpp/simple_url_loader.cc b/services/network/public/cpp/simple_url_loader.cc
index 23e127d..1ac2f96 100644
--- a/services/network/public/cpp/simple_url_loader.cc
+++ b/services/network/public/cpp/simple_url_loader.cc
@@ -1020,28 +1020,28 @@
       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)
-          owns_file_ = true;
-      }
-
-      if (have_path) {
-        // Try to initialize |file_|, creating the file if needed.
+      if (create_temp_file_) {
+        base::FilePath temp_dir;
+        if (base::GetTempDir(&temp_dir)) {
+          file_ = base::CreateAndOpenTemporaryFileInDir(temp_dir, &path_);
+        }
+      } else {
         file_.Initialize(
             path_, base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS);
       }
 
       // If CreateTemporaryFile() or File::Initialize() failed, report failure.
       if (!file_.IsValid()) {
+        net::Error net_error = net::FileErrorToNetError(file_.error_details());
+        if (net_error == net::OK) {
+          net_error = net::MapSystemError(logging::GetLastSystemErrorCode());
+          if (net_error == net::OK) {
+            net_error = net::ERR_FILE_NOT_FOUND;
+          }
+        }
         body_handler_task_runner_->PostTask(
-            FROM_HERE, base::BindOnce(std::move(on_done_callback),
-                                      net::MapSystemError(
-                                          logging::GetLastSystemErrorCode()),
-                                      0, base::FilePath()));
+            FROM_HERE, base::BindOnce(std::move(on_done_callback), net_error, 0,
+                                      base::FilePath()));
         return;
       }
 
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential macOS sandbox escape via TOCTOU in SimpleURLLoader temporary file creation

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A potential Time-of-Check to Time-of-Use (TOCTOU) vulnerability exists in SimpleURLLoader’s temporary file creation mechanism on macOS. A compromised, sandboxed network service process can exploit a race condition in the shared temporary directory to swap a temporary file with a symbolic link. Because the browser process re-opens the file path without symbolic link protection, it may follow the link and overwrite arbitrary files with browser process privileges.

Affected files:

  • services/network/public/cpp/simple_url_loader.cc

Estimated timestamp from git blame: 2017-10-18

Summary

A potential Time-of-Check to Time-of-Use (TOCTOU) vulnerability has been identified in the temporary file writing logic of SimpleURLLoader (services/network/public/cpp/simple_url_loader.cc). When downloading to a temporary file, the code generates and closes a temporary file, and subsequently re-opens the path. On macOS, both the unsandboxed browser process and the sandboxed network service process share access to the same temporary directory, creating a race window where a compromised network service can replace the temporary file with a symbolic link, resulting in an arbitrary file write under browser privileges (sandbox escape).

Vulnerability Analysis

In services/network/public/cpp/simple_url_loader.cc, within FileWriter::StartWritingOnFileSequence (lines 1023-1036):

bool have_path = !create_temp_file_;
if (!have_path) {
  DCHECK(create_temp_file_);
  have_path = base::CreateTemporaryFile(&path_);   // (1) Creates the file and immediately closes its file descriptor
  if (have_path)
    owns_file_ = true;
}

if (have_path) {
  file_.Initialize(
      path_, base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS);
                                                   // (2) Re-opens the file by path
}
  1. Step 1 (base::CreateTemporaryFile): The helper base::CreateTemporaryFile (defined in base/files/file_util.cc, line 395) resolves the temporary directory and calls mkstemp via CreateAndOpenFdForTemporaryFileInDir. It immediately closes the returned file descriptor, leaving only the generated path string in path_ (e.g., /private/var/folders/.../T/.com.google.Chrome.XXXXXX).

  2. Step 2 (file_.Initialize): The browser process re-opens this path on a background thread using file_.Initialize with base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS. On POSIX platforms (including macOS), FLAG_CREATE_ALWAYS maps directly to O_CREAT | O_TRUNC (in base/files/file_posix.cc, lines 573-577). Crucially, the flag map does not employ O_NOFOLLOW or check whether the target is a symbolic link.

Sandbox Context (macOS)

Although the Network Service is sandboxed, its macOS Seatbelt profile (sandbox/policy/mac/network.sb, lines 50-53) explicitly permits read and write access to the per-user temporary directory:

(allow file-read* file-write*
  (subpath (param darwin-user-cache-dir))
  (subpath (param darwin-user-temp-dir))
)

The parameter darwin-user-temp-dir is retrieved in the browser process via confstr(_CS_DARWIN_USER_TEMP_DIR, ...) and passed during the launching of the child process. Therefore, both the unsandboxed browser process (running SimpleURLLoader’s file sequence) and the sandboxed network process share access to the exact same temporary directory path.

Potential Exploitation Scenario

Based on static analysis, a compromised network process could potentially execute the following steps to escape the sandbox:

  1. Monitoring: The compromised network process monitors the darwin-user-temp-dir for the creation of temporary files adhering to the pattern .com.google.Chrome.XXXXXX.
  2. The Race: Immediately after base::CreateTemporaryFile is called and closes its file descriptor (Step 1), but before the browser process calls file_.Initialize (Step 2), the attacker deletes the empty temporary file via unlink().
  3. Symlink Placement: The attacker creates a symbolic link at that exact path pointing to a highly sensitive destination in the user’s home directory (e.g., ~/Library/LaunchAgents/persistence.plist or ~/.zshrc).
  4. Overwrite: When the browser process calls file_.Initialize, the open call follows the symbolic link, truncating the targeted plist or shell script, and writes the incoming network payload directly into it.
  5. Retry Mitigation: If the attacker misses the tight race window, they can abort the loader from the network side with net::ERR_NETWORK_CHANGED. This triggers SimpleURLLoader’s retry mechanism (frequently used by components like the ExtensionDownloader), giving the attacker multiple attempts to win the race.

Note: These are potential/suggested exploitation steps derived from static code and sandbox profile tracing, as our current tooling does not have the capability to execute code or verify runtime behavior.

Suggested Fix

Avoid closing and re-opening the file by path. Instead of calling base::CreateTemporaryFile and then file_.Initialize, utilize an API that keeps the file descriptor open upon creation, such as base::CreateAndOpenTemporaryFileInDir. This ensures that the browser retains exclusive and safe access to the file handle created atomically by mkstemp without leaving a path open to TOCTOU manipulation.

Evaluated with Chrome root at commit: b1520ef4a76878853a31f0943b565e42060edec8


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker