Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in MHTML
DescriptionInappropriate implementation in MHTML
ComponentMHTML
Bug ClassLogic Error
Tracker519991712
Fix commit3e51b8b158a7 (chromium/src) +7/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
if
base/files/file_posix.cc
modified

Files Changed

  • base/files/file.h
  • base/files/file_posix.cc
  • content/browser/download/mhtml_generation_manager.cc
From 3e51b8b158a740ec45104da5e1102759b504e1b1 Mon Sep 17 00:00:00 2001
From: Min Qin <[email protected]>
Date: Mon, 15 Jun 2026 17:03:13 -0700
Subject: [PATCH] [base] Add O_NOFOLLOW when opening a temp file for MHTML download

MHTML generation on macOS uses base::CreateTemporaryFile() which
immediately closes the created file descriptor. This CL adds
`FLAG_NO_FOLLOW` to `base::File::Flags` and implements it on POSIX using
`O_NOFOLLOW`. It then uses this flag when reopening the MHTML file in
`MHTMLGenerationManager`, preventing symlink following

Bug: 519991712
Change-Id: I9aa70954e6ed5e80a3499ae5fabce8b1fba175ad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7933724
Reviewed-by: Lei Zhang <[email protected]>
Commit-Queue: Min Qin <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1647183}
---

diff --git a/base/files/file.h b/base/files/file.h
index 03a28591..9cecfb9 100644
--- a/base/files/file.h
+++ b/base/files/file.h
@@ -81,6 +81,7 @@
         1 << 21,  // Windows only. Marks the file with a deny ACE that prevents
                   // opening the file with EXECUTE access. Cannot be used with
                   // FILE_WIN_EXECUTE flag. See also PreventExecuteMapping.
+    FLAG_NO_FOLLOW = 1 << 22,  // POSIX only. Do not follow symbolic links.
   };
 
   // This enum has been recorded in multiple histograms using PlatformFileError
diff --git a/base/files/file_posix.cc b/base/files/file_posix.cc
index 78d41f1..68a602b 100644
--- a/base/files/file_posix.cc
+++ b/base/files/file_posix.cc
@@ -608,6 +608,10 @@
     open_flags |= O_APPEND | O_WRONLY;
   }
 
+  if (flags & FLAG_NO_FOLLOW) {
+    open_flags |= O_NOFOLLOW;
+  }
+
   static_assert(O_RDONLY == 0, "O_RDONLY must equal zero");
 
   mode_t mode = S_IRUSR | S_IWUSR;
diff --git a/content/browser/download/mhtml_generation_manager.cc b/content/browser/download/mhtml_generation_manager.cc
index e089638..0ff75c0 100644
--- a/content/browser/download/mhtml_generation_manager.cc
+++ b/content/browser/download/mhtml_generation_manager.cc
@@ -98,8 +98,8 @@
   // principals) and forbid seeking/overwriting earlier file contents (as this
   // would allow overwriting content generated by other renderers / other web
   // principals).
-  uint32_t file_flags =
-      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_APPEND;
+  uint32_t file_flags = base::File::FLAG_CREATE_ALWAYS |
+                        base::File::FLAG_APPEND | base::File::FLAG_NO_FOLLOW;
 
   base::File browser_file(file_path, file_flags);
   if (!browser_file.IsValid()) {
Loading diff…

Original Bug Report

reported by [email protected]

Potential macOS TOCTOU in CreateMHTMLFile via shared _CS_DARWIN_USER_TEMP_DIR

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: MHTML generation on macOS uses base::CreateTemporaryFile() which immediately closes the created file descriptor. During the thread hops before the path is reopened by name in the browser process, a compromised sandboxed process sharing the temporary directory can replace the file with a symbolic link. This potentially allows a compromised child process to escalate privileges to perform arbitrary file reads or writes.

Affected files:

  • content/browser/download/mhtml_generation_manager.cc
  • chrome/browser/extensions/api/page_capture/page_capture_api.cc
  • content/browser/devtools/protocol/devtools_mhtml_helper.cc

Estimated timestamp from git blame: 2011-06-14

Description

There is a potential Time-of-Check to Time-of-Use (TOCTOU) vulnerability on macOS during MHTML file generation.

When creating an MHTML snapshot, the browser utilizes base::CreateTemporaryFile() to generate a temporary path. However, this helper function immediately closes the file descriptor (fd) returned by mkstemp(). After several asynchronous thread hops (from the ThreadPool to the IO thread, the UI thread, and finally to the sequential Download task runner), the file path is reopened by name in the browser process via CreateMHTMLFile() without symbolic link protection (i.e., without O_NOFOLLOW or O_EXCL).

Because the browser and several sandboxed child processes (such as the GPU, Network, and On-Device Model processes) share the same macOS per-user temporary directory (_CS_DARWIN_USER_TEMP_DIR), and because those child processes are granted write and unlink permissions within this subtree by their Seatbelt sandbox profiles, a compromised child process can exploit this window to redirect the subsequent browser-privileged write.

Potential Attack Steps

Note: These are suggested/potential steps; our tooling does not currently have the capability to run code to confirm a working proof of concept.

  1. An attacker compromises a sandboxed child process on macOS (such as the GPU or Network process) and starts monitoring the shared _CS_DARWIN_USER_TEMP_DIR directory for newly created temporary files matching the pattern .com.google.Chrome.XXXXXX.
  2. An MHTML generation request is triggered (e.g., via a pageCapture extension or DevTools protocol snapshot).
  3. The browser process creates the temporary file via base::CreateTemporaryFile() and immediately closes the file descriptor.
  4. During the subsequent thread hops, the compromised child process deletes the closed temporary file and replaces it with a symbolic link pointing to a user-writable destination outside the sandbox (for example, ~/Library/LaunchAgents/com.pwn.plist).
  5. The browser process executes CreateMHTMLFile, calling open() on the path. Because the flags do not specify O_NOFOLLOW, the kernel follows the symbolic link, truncating and opening the target file at full browser privilege.
  6. The browser process duplicates this file descriptor and passes it to the renderer, allowing the renderer to write content (such as a launchd plist payload) directly to the target path.

Affected Code Locations

  • Temporary File Creation (Extensions): chrome/browser/extensions/api/page_capture/page_capture_api.cc
  • Temporary File Creation (DevTools): content/browser/devtools/protocol/devtools_mhtml_helper.cc
  • File Open/Sink: content/browser/download/mhtml_generation_manager.cc inside CreateMHTMLFile()

Suggested Fix

Instead of closing the temporary file descriptor and subsequently reopening the file by name, the browser should utilize a utility such as base::CreateAndOpenTemporaryFileInDir() to safely retain the opened file descriptor from the beginning. This file descriptor should then be passed directly to the MHTML generation flow, avoiding any name-based reopening operations and eliminating the TOCTOU window entirely.

Evaluated with Chrome root at commit: 57b021e1fdae94a215627d29aeb1ccf2eb5b3e91


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