[clang] [llvm] Sandboxing v2 (PR #165350)

Jan Svoboda via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 30 15:53:24 PDT 2025


https://github.com/jansvoboda11 updated https://github.com/llvm/llvm-project/pull/165350

>From ce13278e1df2649e7c0c889da4b9eafa258dde06 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 16:00:53 -0700
Subject: [PATCH 01/19] [llvm] Introduce IO sandboxing

---
 llvm/CMakeLists.txt                          |  1 +
 llvm/include/llvm/Config/llvm-config.h.cmake |  3 ++
 llvm/include/llvm/Support/IOSandbox.h        | 39 ++++++++++++++++++++
 3 files changed, 43 insertions(+)
 create mode 100644 llvm/include/llvm/Support/IOSandbox.h

diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
index c450ee5a3d72e..eba10b7a703dd 100644
--- a/llvm/CMakeLists.txt
+++ b/llvm/CMakeLists.txt
@@ -704,6 +704,7 @@ else()
   option(LLVM_ENABLE_ASSERTIONS "Enable assertions" ON)
 endif()
 
+option(LLVM_ENABLE_IO_SANDBOX "Enable IO sandboxing in supported tools" ${LLVM_ENABLE_ASSERTIONS})
 option(LLVM_ENABLE_EXPENSIVE_CHECKS "Enable expensive checks" OFF)
 
 set(LLVM_ABI_BREAKING_CHECKS "WITH_ASSERTS" CACHE STRING
diff --git a/llvm/include/llvm/Config/llvm-config.h.cmake b/llvm/include/llvm/Config/llvm-config.h.cmake
index 6488d6c01b5c6..9ac0115ee2184 100644
--- a/llvm/include/llvm/Config/llvm-config.h.cmake
+++ b/llvm/include/llvm/Config/llvm-config.h.cmake
@@ -126,6 +126,9 @@
  * in non assert builds */
 #cmakedefine01 LLVM_UNREACHABLE_OPTIMIZE
 
+/* Define if building LLVM with LLVM_ENABLE_IO_SANDBOX */
+#cmakedefine01 LLVM_ENABLE_IO_SANDBOX
+
 /* Define to 1 if you have the DIA SDK installed, and to 0 if you don't. */
 #cmakedefine01 LLVM_ENABLE_DIA_SDK
 
diff --git a/llvm/include/llvm/Support/IOSandbox.h b/llvm/include/llvm/Support/IOSandbox.h
new file mode 100644
index 0000000000000..ccd8af24273da
--- /dev/null
+++ b/llvm/include/llvm/Support/IOSandbox.h
@@ -0,0 +1,39 @@
+//===- IOSandbox.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_IOSANDBOX_H
+#define LLVM_SUPPORT_IOSANDBOX_H
+
+#ifdef LLVM_ENABLE_IO_SANDBOX
+
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/SaveAndRestore.h"
+
+namespace llvm::sys::sandbox {
+inline thread_local bool Enabled = false;
+using ScopedSetting = SaveAndRestore<bool>;
+inline SaveAndRestore<bool> scopedEnable() { return {Enabled, true}; }
+inline SaveAndRestore<bool> scopedDisable() { return {Enabled, false}; }
+inline void violationIfEnabled() {
+  if (Enabled)
+    reportFatalInternalError("IO sandbox violation");
+}
+} // namespace llvm::sys::sandbox
+
+#else
+
+namespace llvm::sys::sandbox {
+using ScopedSetting = bool;
+inline ScopedSetting scopedEnable() { return true; }
+inline ScopedSetting scopedDisable() { return true; }
+inline void violationIfEnabled() {}
+} // namespace llvm::sys::sandbox
+
+#endif
+
+#endif

>From 150ed6ce111c5acdb9397eb8799dad7f74b24d53 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 16:01:48 -0700
Subject: [PATCH 02/19] [clang] Enable IO sandboxing in -cc1 and -cc1as

---
 clang/lib/Driver/Job.cpp          |  5 +++++
 clang/tools/driver/cc1_main.cpp   | 27 ++++++++++++++++++---------
 clang/tools/driver/cc1as_main.cpp | 13 ++++++++++---
 llvm/lib/Support/Signals.cpp      |  4 ++++
 4 files changed, 37 insertions(+), 12 deletions(-)

diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp
index 715429bcd2096..e52d1069b4209 100644
--- a/clang/lib/Driver/Job.cpp
+++ b/clang/lib/Driver/Job.cpp
@@ -21,6 +21,7 @@
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/CrashRecoveryContext.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/Program.h"
@@ -426,6 +427,10 @@ int CC1Command::Execute(ArrayRef<std::optional<StringRef>> Redirects,
   if (ExecutionFailed)
     *ExecutionFailed = false;
 
+  // Enabling the sandbox here allows us to restore its previous state even when
+  // this cc1 invocation crashes.
+  [[maybe_unused]] auto EnableSandbox = llvm::sys::sandbox::scopedEnable();
+
   llvm::CrashRecoveryContext CRC;
   CRC.DumpStackAndCleanupOnFailure = true;
 
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index 52cffa4ccbe1f..8a20c7df5f596 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -38,6 +38,7 @@
 #include "llvm/Support/BuryPointer.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Process.h"
@@ -272,7 +273,11 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
       CompilerInvocation::GetResourcesPath(Argv0, MainAddr);
 
   /// Create the actual file system.
-  Clang->createVirtualFileSystem(llvm::vfs::getRealFileSystem(), DiagsBuffer);
+  auto VFS = [] {
+    [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+    return llvm::vfs::getRealFileSystem();
+  }();
+  Clang->createVirtualFileSystem(std::move(VFS), DiagsBuffer);
 
   // Create the actual diagnostics engine.
   Clang->createDiagnostics();
@@ -302,15 +307,19 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
 
   // If any timers were active but haven't been destroyed yet, print their
   // results now.  This happens in -disable-free mode.
-  std::unique_ptr<raw_ostream> IOFile = llvm::CreateInfoOutputFile();
-  if (Clang->getCodeGenOpts().TimePassesJson) {
-    *IOFile << "{\n";
-    llvm::TimerGroup::printAllJSONValues(*IOFile, "");
-    *IOFile << "\n}\n";
-  } else if (!Clang->getCodeGenOpts().TimePassesStatsFile) {
-    llvm::TimerGroup::printAll(*IOFile);
+  {
+    // This isn't a formal input or output of the compiler.
+    [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+    std::unique_ptr<raw_ostream> IOFile = llvm::CreateInfoOutputFile();
+    if (Clang->getCodeGenOpts().TimePassesJson) {
+      *IOFile << "{\n";
+      llvm::TimerGroup::printAllJSONValues(*IOFile, "");
+      *IOFile << "\n}\n";
+    } else if (!Clang->getCodeGenOpts().TimePassesStatsFile) {
+      llvm::TimerGroup::printAll(*IOFile);
+    }
+    llvm::TimerGroup::clearAll();
   }
-  llvm::TimerGroup::clearAll();
 
   if (llvm::timeTraceProfilerEnabled()) {
     if (auto profilerOutput = Clang->createOutputFile(
diff --git a/clang/tools/driver/cc1as_main.cpp b/clang/tools/driver/cc1as_main.cpp
index 50da2f8449a22..589b7304e8565 100644
--- a/clang/tools/driver/cc1as_main.cpp
+++ b/clang/tools/driver/cc1as_main.cpp
@@ -45,6 +45,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Process.h"
@@ -425,8 +426,11 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
   if (!TheTarget)
     return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple.str();
 
-  ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
-      MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
+  ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = [&] {
+    // FIXME(sandboxing): Make this a proper input file.
+    [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+    return MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
+  }();
 
   if (std::error_code EC = Buffer.getError()) {
     return Diags.Report(diag::err_fe_error_reading)
@@ -672,7 +676,10 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
   DiagClient->setPrefix("clang -cc1as");
   DiagnosticsEngine Diags(DiagnosticIDs::create(), DiagOpts, DiagClient);
 
-  auto VFS = vfs::getRealFileSystem();
+  auto VFS = [] {
+    [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+    return vfs::getRealFileSystem();
+  }();
 
   // Set an error handler, so that any LLVM backend diagnostics go through our
   // error handler.
diff --git a/llvm/lib/Support/Signals.cpp b/llvm/lib/Support/Signals.cpp
index f8a14a45ddc3e..786c4576f13e3 100644
--- a/llvm/lib/Support/Signals.cpp
+++ b/llvm/lib/Support/Signals.cpp
@@ -23,6 +23,7 @@
 #include "llvm/Support/FileUtilities.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
@@ -95,6 +96,9 @@ CallBacksToRun() {
 
 // Signal-safe.
 void sys::RunSignalHandlers() {
+  // Let's not interfere with stack trace symbolication and friends.
+  [[maybe_unused]] auto BypassSandbox = sandbox::scopedDisable();
+
   for (CallbackAndCookie &RunMe : CallBacksToRun()) {
     auto Expected = CallbackAndCookie::Status::Initialized;
     auto Desired = CallbackAndCookie::Status::Executing;

>From 2c7996ef19ea14092d713a6a69738c4ef4e710e5 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 16:09:57 -0700
Subject: [PATCH 03/19] [Support] Disallow `MemoryBuffer::getFile*()` in IO
 sandbox

---
 llvm/lib/Support/MemoryBuffer.cpp | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/llvm/lib/Support/MemoryBuffer.cpp b/llvm/lib/Support/MemoryBuffer.cpp
index 1c4645ad83641..c64d88d9f5067 100644
--- a/llvm/lib/Support/MemoryBuffer.cpp
+++ b/llvm/lib/Support/MemoryBuffer.cpp
@@ -161,6 +161,8 @@ ErrorOr<std::unique_ptr<MemoryBuffer>>
 MemoryBuffer::getFileOrSTDIN(const Twine &Filename, bool IsText,
                              bool RequiresNullTerminator,
                              std::optional<Align> Alignment) {
+  sys::sandbox::violationIfEnabled();
+
   SmallString<256> NameBuf;
   StringRef NameRef = Filename.toStringRef(NameBuf);
 
@@ -174,6 +176,8 @@ ErrorOr<std::unique_ptr<MemoryBuffer>>
 MemoryBuffer::getFileSlice(const Twine &FilePath, uint64_t MapSize,
                            uint64_t Offset, bool IsVolatile,
                            std::optional<Align> Alignment) {
+  sys::sandbox::violationIfEnabled();
+
   return getFileAux<MemoryBuffer>(FilePath, MapSize, Offset, /*IsText=*/false,
                                   /*RequiresNullTerminator=*/false, IsVolatile,
                                   Alignment);
@@ -258,6 +262,8 @@ ErrorOr<std::unique_ptr<MemoryBuffer>>
 MemoryBuffer::getFile(const Twine &Filename, bool IsText,
                       bool RequiresNullTerminator, bool IsVolatile,
                       std::optional<Align> Alignment) {
+  sys::sandbox::violationIfEnabled();
+
   return getFileAux<MemoryBuffer>(Filename, /*MapSize=*/-1, /*Offset=*/0,
                                   IsText, RequiresNullTerminator, IsVolatile,
                                   Alignment);
@@ -288,6 +294,8 @@ getFileAux(const Twine &Filename, uint64_t MapSize, uint64_t Offset,
 ErrorOr<std::unique_ptr<WritableMemoryBuffer>>
 WritableMemoryBuffer::getFile(const Twine &Filename, bool IsVolatile,
                               std::optional<Align> Alignment) {
+  sys::sandbox::violationIfEnabled();
+
   return getFileAux<WritableMemoryBuffer>(
       Filename, /*MapSize=*/-1, /*Offset=*/0, /*IsText=*/false,
       /*RequiresNullTerminator=*/false, IsVolatile, Alignment);
@@ -297,6 +305,8 @@ ErrorOr<std::unique_ptr<WritableMemoryBuffer>>
 WritableMemoryBuffer::getFileSlice(const Twine &Filename, uint64_t MapSize,
                                    uint64_t Offset, bool IsVolatile,
                                    std::optional<Align> Alignment) {
+  sys::sandbox::violationIfEnabled();
+
   return getFileAux<WritableMemoryBuffer>(
       Filename, MapSize, Offset, /*IsText=*/false,
       /*RequiresNullTerminator=*/false, IsVolatile, Alignment);
@@ -455,6 +465,8 @@ getReadWriteFile(const Twine &Filename, uint64_t FileSize, uint64_t MapSize,
 
 ErrorOr<std::unique_ptr<WriteThroughMemoryBuffer>>
 WriteThroughMemoryBuffer::getFile(const Twine &Filename, int64_t FileSize) {
+  sys::sandbox::violationIfEnabled();
+
   return getReadWriteFile(Filename, FileSize, FileSize, 0);
 }
 
@@ -462,6 +474,8 @@ WriteThroughMemoryBuffer::getFile(const Twine &Filename, int64_t FileSize) {
 ErrorOr<std::unique_ptr<WriteThroughMemoryBuffer>>
 WriteThroughMemoryBuffer::getFileSlice(const Twine &Filename, uint64_t MapSize,
                                        uint64_t Offset) {
+  sys::sandbox::violationIfEnabled();
+
   return getReadWriteFile(Filename, -1, MapSize, Offset);
 }
 
@@ -554,6 +568,8 @@ ErrorOr<std::unique_ptr<MemoryBuffer>>
 MemoryBuffer::getOpenFile(sys::fs::file_t FD, const Twine &Filename,
                           uint64_t FileSize, bool RequiresNullTerminator,
                           bool IsVolatile, std::optional<Align> Alignment) {
+  sys::sandbox::violationIfEnabled();
+
   return getOpenFileImpl<MemoryBuffer>(FD, Filename, FileSize, FileSize, 0,
                                        RequiresNullTerminator, IsVolatile,
                                        Alignment);
@@ -563,11 +579,16 @@ ErrorOr<std::unique_ptr<MemoryBuffer>> MemoryBuffer::getOpenFileSlice(
     sys::fs::file_t FD, const Twine &Filename, uint64_t MapSize, int64_t Offset,
     bool IsVolatile, std::optional<Align> Alignment) {
   assert(MapSize != uint64_t(-1));
+
+  sys::sandbox::violationIfEnabled();
+
   return getOpenFileImpl<MemoryBuffer>(FD, Filename, -1, MapSize, Offset, false,
                                        IsVolatile, Alignment);
 }
 
 ErrorOr<std::unique_ptr<MemoryBuffer>> MemoryBuffer::getSTDIN() {
+  sys::sandbox::violationIfEnabled();
+
   // Read in all of the data from stdin, we cannot mmap stdin.
   //
   // FIXME: That isn't necessarily true, we should try to mmap stdin and
@@ -579,6 +600,8 @@ ErrorOr<std::unique_ptr<MemoryBuffer>> MemoryBuffer::getSTDIN() {
 
 ErrorOr<std::unique_ptr<MemoryBuffer>>
 MemoryBuffer::getFileAsStream(const Twine &Filename) {
+  sys::sandbox::violationIfEnabled();
+
   Expected<sys::fs::file_t> FDOrErr =
       sys::fs::openNativeFileForRead(Filename, sys::fs::OF_None);
   if (!FDOrErr)

>From 670cf8cc85f67d5931135d10743ecc6b33cdaa80 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 16:12:29 -0700
Subject: [PATCH 04/19] [Support] Disallow `vfs::{create,get}RealFileSystem()`
 in IO sandbox

---
 llvm/lib/Support/VirtualFileSystem.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp
index c754b30d8de4a..547e6c9abf57b 100644
--- a/llvm/lib/Support/VirtualFileSystem.cpp
+++ b/llvm/lib/Support/VirtualFileSystem.cpp
@@ -31,6 +31,7 @@
 #include "llvm/Support/ErrorOr.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FileSystem/UniqueID.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/SMLoc.h"
@@ -399,10 +400,14 @@ void RealFileSystem::printImpl(raw_ostream &OS, PrintType Type,
 IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
   static IntrusiveRefCntPtr<FileSystem> FS =
       makeIntrusiveRefCnt<RealFileSystem>(true);
+  sys::sandbox::violationIfEnabled();
+
   return FS;
 }
 
 std::unique_ptr<FileSystem> vfs::createPhysicalFileSystem() {
+  sys::sandbox::violationIfEnabled();
+
   return std::make_unique<RealFileSystem>(false);
 }
 

>From f4242fcb79d1b8c6396afbbb0d563309c41024cc Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 16:35:26 -0700
Subject: [PATCH 05/19] [Support] Disallow input `sys::fs` APIs in IO sandbox

---
 llvm/include/llvm/Support/FileSystem.h | 11 +++++++
 llvm/lib/Support/Path.cpp              | 20 +++++++++++++
 llvm/lib/Support/Unix/Path.inc         | 37 ++++++++++++++++++++++++
 llvm/lib/Support/Windows/Path.inc      | 40 ++++++++++++++++++++++++--
 4 files changed, 105 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/Support/FileSystem.h b/llvm/include/llvm/Support/FileSystem.h
index cf2a8104ac813..fa8753d146e22 100644
--- a/llvm/include/llvm/Support/FileSystem.h
+++ b/llvm/include/llvm/Support/FileSystem.h
@@ -36,6 +36,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/ErrorOr.h"
 #include "llvm/Support/FileSystem/UniqueID.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/MD5.h"
 #include <cassert>
 #include <cstdint>
@@ -1447,6 +1448,8 @@ class directory_iterator {
   explicit directory_iterator(const Twine &path, std::error_code &ec,
                               bool follow_symlinks = true)
       : FollowSymlinks(follow_symlinks) {
+    sandbox::violationIfEnabled();
+
     State = std::make_shared<detail::DirIterState>();
     SmallString<128> path_storage;
     ec = detail::directory_iterator_construct(
@@ -1456,6 +1459,8 @@ class directory_iterator {
   explicit directory_iterator(const directory_entry &de, std::error_code &ec,
                               bool follow_symlinks = true)
       : FollowSymlinks(follow_symlinks) {
+    sandbox::violationIfEnabled();
+
     State = std::make_shared<detail::DirIterState>();
     ec = detail::directory_iterator_construct(
         *State, de.path(), FollowSymlinks);
@@ -1466,6 +1471,8 @@ class directory_iterator {
 
   // No operator++ because we need error_code.
   directory_iterator &increment(std::error_code &ec) {
+    sandbox::violationIfEnabled();
+
     ec = directory_iterator_increment(*State);
     return *this;
   }
@@ -1511,6 +1518,8 @@ class recursive_directory_iterator {
                                         bool follow_symlinks = true)
       : State(std::make_shared<detail::RecDirIterState>()),
         Follow(follow_symlinks) {
+    sandbox::violationIfEnabled();
+
     State->Stack.push_back(directory_iterator(path, ec, Follow));
     if (State->Stack.back() == directory_iterator())
       State.reset();
@@ -1518,6 +1527,8 @@ class recursive_directory_iterator {
 
   // No operator++ because we need error_code.
   recursive_directory_iterator &increment(std::error_code &ec) {
+    sandbox::violationIfEnabled();
+
     const directory_iterator end_itr = {};
 
     if (State->HasNoPushRequest)
diff --git a/llvm/lib/Support/Path.cpp b/llvm/lib/Support/Path.cpp
index 3e066665f4155..16d506d21927f 100644
--- a/llvm/lib/Support/Path.cpp
+++ b/llvm/lib/Support/Path.cpp
@@ -834,6 +834,8 @@ bool remove_dots(SmallVectorImpl<char> &the_path, bool remove_dot_dot,
 namespace fs {
 
 std::error_code getUniqueID(const Twine Path, UniqueID &Result) {
+  sandbox::violationIfEnabled();
+
   file_status Status;
   std::error_code EC = status(Path, Status);
   if (EC)
@@ -953,6 +955,8 @@ getPotentiallyUniqueTempFileName(const Twine &Prefix, StringRef Suffix,
 }
 
 std::error_code make_absolute(SmallVectorImpl<char> &path) {
+  sandbox::violationIfEnabled();
+
   if (path::is_absolute(path))
     return {};
 
@@ -1045,6 +1049,8 @@ std::error_code copy_file(const Twine &From, int ToFD) {
 }
 
 ErrorOr<MD5::MD5Result> md5_contents(int FD) {
+  sandbox::violationIfEnabled();
+
   MD5 Hash;
 
   constexpr size_t BufSize = 4096;
@@ -1065,6 +1071,8 @@ ErrorOr<MD5::MD5Result> md5_contents(int FD) {
 }
 
 ErrorOr<MD5::MD5Result> md5_contents(const Twine &Path) {
+  sandbox::violationIfEnabled();
+
   int FD;
   if (auto EC = openFileForRead(Path, FD, OF_None))
     return EC;
@@ -1094,6 +1102,8 @@ bool is_directory(const basic_file_status &status) {
 }
 
 std::error_code is_directory(const Twine &path, bool &result) {
+  sandbox::violationIfEnabled();
+
   file_status st;
   if (std::error_code ec = status(path, st))
     return ec;
@@ -1106,6 +1116,8 @@ bool is_regular_file(const basic_file_status &status) {
 }
 
 std::error_code is_regular_file(const Twine &path, bool &result) {
+  sandbox::violationIfEnabled();
+
   file_status st;
   if (std::error_code ec = status(path, st))
     return ec;
@@ -1118,6 +1130,8 @@ bool is_symlink_file(const basic_file_status &status) {
 }
 
 std::error_code is_symlink_file(const Twine &path, bool &result) {
+  sandbox::violationIfEnabled();
+
   file_status st;
   if (std::error_code ec = status(path, st, false))
     return ec;
@@ -1132,6 +1146,8 @@ bool is_other(const basic_file_status &status) {
 }
 
 std::error_code is_other(const Twine &Path, bool &Result) {
+  sandbox::violationIfEnabled();
+
   file_status FileStatus;
   if (std::error_code EC = status(Path, FileStatus))
     return EC;
@@ -1149,6 +1165,8 @@ void directory_entry::replace_filename(const Twine &Filename, file_type Type,
 }
 
 ErrorOr<perms> getPermissions(const Twine &Path) {
+  sandbox::violationIfEnabled();
+
   file_status Status;
   if (std::error_code EC = status(Path, Status))
     return EC;
@@ -1173,6 +1191,8 @@ const char *mapped_file_region::const_data() const {
 
 Error readNativeFileToEOF(file_t FileHandle, SmallVectorImpl<char> &Buffer,
                           ssize_t ChunkSize) {
+  sandbox::violationIfEnabled();
+
   // Install a handler to truncate the buffer to the correct size on exit.
   size_t Size = Buffer.size();
   auto TruncateOnExit = make_scope_exit([&]() { Buffer.truncate(Size); });
diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc
index 0d991ead72416..ab4d2e33213a5 100644
--- a/llvm/lib/Support/Unix/Path.inc
+++ b/llvm/lib/Support/Unix/Path.inc
@@ -366,6 +366,8 @@ ErrorOr<space_info> disk_space(const Twine &Path) {
 }
 
 std::error_code current_path(SmallVectorImpl<char> &result) {
+  sandbox::violationIfEnabled();
+
   result.clear();
 
   const char *pwd = ::getenv("PWD");
@@ -399,6 +401,8 @@ std::error_code current_path(SmallVectorImpl<char> &result) {
 }
 
 std::error_code set_current_path(const Twine &path) {
+  sandbox::violationIfEnabled();
+
   SmallString<128> path_storage;
   StringRef p = path.toNullTerminatedStringRef(path_storage);
 
@@ -561,6 +565,8 @@ static bool is_local_impl(struct STATVFS &Vfs) {
 }
 
 std::error_code is_local(const Twine &Path, bool &Result) {
+  sandbox::violationIfEnabled();
+
   struct STATVFS Vfs;
   if (::STATVFS(const_cast<char *>(Path.str().c_str()), &Vfs))
     return errnoAsErrorCode();
@@ -570,6 +576,8 @@ std::error_code is_local(const Twine &Path, bool &Result) {
 }
 
 std::error_code is_local(int FD, bool &Result) {
+  sandbox::violationIfEnabled();
+
   struct STATVFS Vfs;
   if (::FSTATVFS(FD, &Vfs))
     return errnoAsErrorCode();
@@ -618,6 +626,8 @@ static int convertAccessMode(AccessMode Mode) {
 }
 
 std::error_code access(const Twine &Path, AccessMode Mode) {
+  sandbox::violationIfEnabled();
+
   SmallString<128> PathStorage;
   StringRef P = Path.toNullTerminatedStringRef(PathStorage);
 
@@ -637,6 +647,8 @@ std::error_code access(const Twine &Path, AccessMode Mode) {
 }
 
 bool can_execute(const Twine &Path) {
+  sandbox::violationIfEnabled();
+
   return !access(Path, AccessMode::Execute);
 }
 
@@ -646,6 +658,8 @@ bool equivalent(file_status A, file_status B) {
 }
 
 std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
+  sandbox::violationIfEnabled();
+
   file_status fsA, fsB;
   if (std::error_code ec = status(A, fsA))
     return ec;
@@ -760,6 +774,8 @@ static std::error_code fillStatus(int StatRet, const struct stat &Status,
 }
 
 std::error_code status(const Twine &Path, file_status &Result, bool Follow) {
+  sandbox::violationIfEnabled();
+
   SmallString<128> PathStorage;
   StringRef P = Path.toNullTerminatedStringRef(PathStorage);
 
@@ -769,6 +785,8 @@ std::error_code status(const Twine &Path, file_status &Result, bool Follow) {
 }
 
 std::error_code status(int FD, file_status &Result) {
+  sandbox::violationIfEnabled();
+
   struct stat Status;
   int StatRet = ::fstat(FD, &Status);
   return fillStatus(StatRet, Status, Result);
@@ -870,6 +888,8 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset,
 mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
                                        uint64_t offset, std::error_code &ec)
     : Size(length), Mode(mode) {
+  sandbox::violationIfEnabled();
+
   (void)Mode;
   ec = init(fd, offset, mode);
   if (ec)
@@ -958,6 +978,8 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &It) {
 }
 
 ErrorOr<basic_file_status> directory_entry::status() const {
+  sandbox::violationIfEnabled();
+
   file_status s;
   if (auto EC = fs::status(Path, s, FollowSymlinks))
     return EC;
@@ -1027,6 +1049,8 @@ static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags,
 std::error_code openFile(const Twine &Name, int &ResultFD,
                          CreationDisposition Disp, FileAccess Access,
                          OpenFlags Flags, unsigned Mode) {
+  sandbox::violationIfEnabled();
+
   int OpenFlags = nativeOpenFlags(Disp, Flags, Access);
 
   SmallString<128> Storage;
@@ -1131,6 +1155,7 @@ std::error_code openFile(const Twine &Name, int &ResultFD,
 Expected<int> openNativeFile(const Twine &Name, CreationDisposition Disp,
                              FileAccess Access, OpenFlags Flags,
                              unsigned Mode) {
+  sandbox::violationIfEnabled();
 
   int FD;
   std::error_code EC = openFile(Name, FD, Disp, Access, Flags, Mode);
@@ -1142,6 +1167,8 @@ Expected<int> openNativeFile(const Twine &Name, CreationDisposition Disp,
 std::error_code openFileForRead(const Twine &Name, int &ResultFD,
                                 OpenFlags Flags,
                                 SmallVectorImpl<char> *RealPath) {
+  sandbox::violationIfEnabled();
+
   std::error_code EC =
       openFile(Name, ResultFD, CD_OpenExisting, FA_Read, Flags, 0666);
   if (EC)
@@ -1183,6 +1210,8 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD,
 
 Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags,
                                        SmallVectorImpl<char> *RealPath) {
+  sandbox::violationIfEnabled();
+
   file_t ResultFD;
   std::error_code EC = openFileForRead(Name, ResultFD, Flags, RealPath);
   if (EC)
@@ -1195,6 +1224,8 @@ file_t getStdoutHandle() { return 1; }
 file_t getStderrHandle() { return 2; }
 
 Expected<size_t> readNativeFile(file_t FD, MutableArrayRef<char> Buf) {
+  sandbox::violationIfEnabled();
+
 #if defined(__APPLE__)
   size_t Size = std::min<size_t>(Buf.size(), INT32_MAX);
 #else
@@ -1217,6 +1248,8 @@ Expected<size_t> readNativeFile(file_t FD, MutableArrayRef<char> Buf) {
 
 Expected<size_t> readNativeFileSlice(file_t FD, MutableArrayRef<char> Buf,
                                      uint64_t Offset) {
+  sandbox::violationIfEnabled();
+
 #if defined(__APPLE__)
   size_t Size = std::min<size_t>(Buf.size(), INT32_MAX);
 #else
@@ -1296,6 +1329,8 @@ std::error_code unlockFile(int FD) {
 }
 
 std::error_code closeFile(file_t &F) {
+  sandbox::violationIfEnabled();
+
   file_t TmpF = F;
   F = kInvalidFile;
   return Process::SafelyCloseFileDescriptor(TmpF);
@@ -1343,6 +1378,8 @@ std::error_code remove_directories(const Twine &path, bool IgnoreErrors) {
 
 std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest,
                           bool expand_tilde) {
+  sandbox::violationIfEnabled();
+
   dest.clear();
   if (path.isTriviallyEmpty())
     return std::error_code();
diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc
index be007b7abdb51..f848aec030cd6 100644
--- a/llvm/lib/Support/Windows/Path.inc
+++ b/llvm/lib/Support/Windows/Path.inc
@@ -198,6 +198,8 @@ TimePoint<> basic_file_status::getLastModificationTime() const {
 uint32_t file_status::getLinkCount() const { return NumLinks; }
 
 std::error_code current_path(SmallVectorImpl<char> &result) {
+  sandbox::violationIfEnabled();
+
   SmallVector<wchar_t, MAX_PATH> cur_path;
   DWORD len = MAX_PATH;
 
@@ -226,6 +228,8 @@ std::error_code current_path(SmallVectorImpl<char> &result) {
 }
 
 std::error_code set_current_path(const Twine &path) {
+  sandbox::violationIfEnabled();
+
   // Convert to utf-16.
   SmallVector<wchar_t, 128> wide_path;
   if (std::error_code ec = widenPath(path, wide_path))
@@ -350,6 +354,8 @@ static std::error_code is_local_internal(SmallVectorImpl<wchar_t> &Path,
 }
 
 std::error_code is_local(const Twine &path, bool &result) {
+  sandbox::violationIfEnabled();
+
   if (!llvm::sys::fs::exists(path) || !llvm::sys::path::has_root_path(path))
     return make_error_code(errc::no_such_file_or_directory);
 
@@ -413,6 +419,8 @@ static std::error_code realPathFromHandle(HANDLE H,
 }
 
 std::error_code is_local(int FD, bool &Result) {
+  sandbox::violationIfEnabled();
+
   SmallVector<wchar_t, 128> FinalPath;
   HANDLE Handle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
 
@@ -635,6 +643,8 @@ std::error_code resize_file_sparse(int FD, uint64_t Size) {
 }
 
 std::error_code access(const Twine &Path, AccessMode Mode) {
+  sandbox::violationIfEnabled();
+
   SmallVector<wchar_t, 128> PathUtf16;
 
   if (std::error_code EC = widenPath(Path, PathUtf16))
@@ -674,6 +684,8 @@ bool equivalent(file_status A, file_status B) {
 }
 
 std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
+  sandbox::violationIfEnabled();
+
   file_status fsA, fsB;
   if (std::error_code ec = status(A, fsA))
     return ec;
@@ -789,6 +801,8 @@ handle_status_error:
 }
 
 std::error_code status(const Twine &path, file_status &result, bool Follow) {
+  sandbox::violationIfEnabled();
+
   SmallString<128> path_storage;
   SmallVector<wchar_t, 128> path_utf16;
 
@@ -823,11 +837,15 @@ std::error_code status(const Twine &path, file_status &result, bool Follow) {
 }
 
 std::error_code status(int FD, file_status &Result) {
+  sandbox::violationIfEnabled();
+
   HANDLE FileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
   return getStatus(FileHandle, Result);
 }
 
 std::error_code status(file_t FileHandle, file_status &Result) {
+  sandbox::violationIfEnabled();
+
   return getStatus(FileHandle, Result);
 }
 
@@ -957,6 +975,8 @@ mapped_file_region::mapped_file_region(sys::fs::file_t fd, mapmode mode,
                                        size_t length, uint64_t offset,
                                        std::error_code &ec)
     : Size(length) {
+  sandbox::violationIfEnabled();
+
   ec = init(fd, offset, mode);
   if (ec)
     copyFrom(mapped_file_region());
@@ -1242,6 +1262,8 @@ static std::error_code openNativeFileInternal(const Twine &Name,
 Expected<file_t> openNativeFile(const Twine &Name, CreationDisposition Disp,
                                 FileAccess Access, OpenFlags Flags,
                                 unsigned Mode) {
+  sandbox::violationIfEnabled();
+
   // Verify that we don't have both "append" and "excl".
   assert((!(Disp == CD_CreateNew) || !(Flags & OF_Append)) &&
          "Cannot specify both 'CreateNew' and 'Append' file creation flags!");
@@ -1277,6 +1299,8 @@ Expected<file_t> openNativeFile(const Twine &Name, CreationDisposition Disp,
 std::error_code openFile(const Twine &Name, int &ResultFD,
                          CreationDisposition Disp, FileAccess Access,
                          OpenFlags Flags, unsigned int Mode) {
+  sandbox::violationIfEnabled();
+
   Expected<file_t> Result = openNativeFile(Name, Disp, Access, Flags);
   if (!Result)
     return errorToErrorCode(Result.takeError());
@@ -1300,12 +1324,16 @@ static std::error_code directoryRealPath(const Twine &Name,
 std::error_code openFileForRead(const Twine &Name, int &ResultFD,
                                 OpenFlags Flags,
                                 SmallVectorImpl<char> *RealPath) {
+  sandbox::violationIfEnabled();
+
   Expected<HANDLE> NativeFile = openNativeFileForRead(Name, Flags, RealPath);
   return nativeFileToFd(std::move(NativeFile), ResultFD, OF_None);
 }
 
 Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags,
                                        SmallVectorImpl<char> *RealPath) {
+  sandbox::violationIfEnabled();
+
   Expected<file_t> Result =
       openNativeFile(Name, CD_OpenExisting, FA_Read, Flags);
 
@@ -1324,9 +1352,9 @@ file_t getStdinHandle() { return ::GetStdHandle(STD_INPUT_HANDLE); }
 file_t getStdoutHandle() { return ::GetStdHandle(STD_OUTPUT_HANDLE); }
 file_t getStderrHandle() { return ::GetStdHandle(STD_ERROR_HANDLE); }
 
-Expected<size_t> readNativeFileImpl(file_t FileHandle,
-                                    MutableArrayRef<char> Buf,
-                                    OVERLAPPED *Overlap) {
+static Expected<size_t> readNativeFileImpl(file_t FileHandle,
+                                           MutableArrayRef<char> Buf,
+                                           OVERLAPPED *Overlap) {
   // ReadFile can only read 2GB at a time. The caller should check the number of
   // bytes and read in a loop until termination.
   DWORD BytesToRead =
@@ -1342,12 +1370,16 @@ Expected<size_t> readNativeFileImpl(file_t FileHandle,
 }
 
 Expected<size_t> readNativeFile(file_t FileHandle, MutableArrayRef<char> Buf) {
+  sandbox::violationIfEnabled();
+
   return readNativeFileImpl(FileHandle, Buf, /*Overlap=*/nullptr);
 }
 
 Expected<size_t> readNativeFileSlice(file_t FileHandle,
                                      MutableArrayRef<char> Buf,
                                      uint64_t Offset) {
+  sandbox::violationIfEnabled();
+
   OVERLAPPED Overlapped = {};
   Overlapped.Offset = uint32_t(Offset);
   Overlapped.OffsetHigh = uint32_t(Offset >> 32);
@@ -1492,6 +1524,8 @@ void expand_tilde(const Twine &path, SmallVectorImpl<char> &dest) {
 
 std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest,
                           bool expand_tilde) {
+  sandbox::violationIfEnabled();
+
   dest.clear();
   if (path.isTriviallyEmpty())
     return std::error_code();

>From acf2f4e1c190c0ccc82d3265052d2e5fe41cd24d Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 16:40:27 -0700
Subject: [PATCH 06/19] [Support] Bless `vfs::RealFileSystem` and friends in IO
 sandbox

---
 llvm/lib/Support/VirtualFileSystem.cpp | 33 ++++++++++++++++++++++++--
 1 file changed, 31 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp
index 547e6c9abf57b..05353a23a066c 100644
--- a/llvm/lib/Support/VirtualFileSystem.cpp
+++ b/llvm/lib/Support/VirtualFileSystem.cpp
@@ -220,6 +220,8 @@ class RealFile : public File {
 RealFile::~RealFile() { close(); }
 
 ErrorOr<Status> RealFile::status() {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   assert(FD != kInvalidFile && "cannot stat closed file");
   if (!S.isStatusKnown()) {
     file_status RealStatus;
@@ -237,18 +239,24 @@ ErrorOr<std::string> RealFile::getName() {
 ErrorOr<std::unique_ptr<MemoryBuffer>>
 RealFile::getBuffer(const Twine &Name, int64_t FileSize,
                     bool RequiresNullTerminator, bool IsVolatile) {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   assert(FD != kInvalidFile && "cannot get buffer for closed file");
   return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
                                    IsVolatile);
 }
 
 std::error_code RealFile::close() {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   std::error_code EC = sys::fs::closeFile(FD);
   FD = kInvalidFile;
   return EC;
 }
 
 void RealFile::setPath(const Twine &Path) {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   RealName = Path.str();
   if (auto Status = status())
     S = Status.get().copyWithNewName(Status.get(), Path);
@@ -328,6 +336,8 @@ class RealFileSystem : public FileSystem {
 } // namespace
 
 ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   SmallString<256> Storage;
   sys::fs::file_status RealStatus;
   if (std::error_code EC =
@@ -338,15 +348,21 @@ ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
 
 ErrorOr<std::unique_ptr<File>>
 RealFileSystem::openFileForRead(const Twine &Name) {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   return openFileForReadWithFlags(Name, sys::fs::OF_Text);
 }
 
 ErrorOr<std::unique_ptr<File>>
 RealFileSystem::openFileForReadBinary(const Twine &Name) {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   return openFileForReadWithFlags(Name, sys::fs::OF_None);
 }
 
 llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   if (WD && *WD)
     return std::string(WD->get().Specified);
   if (WD)
@@ -359,6 +375,8 @@ llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
 }
 
 std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   if (!WD)
     return llvm::sys::fs::set_current_path(Path);
 
@@ -376,12 +394,16 @@ std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
 }
 
 std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   SmallString<256> Storage;
   return llvm::sys::fs::is_local(adjustPath(Path, Storage), Result);
 }
 
 std::error_code RealFileSystem::getRealPath(const Twine &Path,
                                             SmallVectorImpl<char> &Output) {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   SmallString<256> Storage;
   return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output);
 }
@@ -417,12 +439,17 @@ class RealFSDirIter : public llvm::vfs::detail::DirIterImpl {
   llvm::sys::fs::directory_iterator Iter;
 
 public:
-  RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) {
-    if (Iter != llvm::sys::fs::directory_iterator())
+  RealFSDirIter(const Twine &Path, std::error_code &EC) {
+    [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
+    Iter = sys::fs::directory_iterator(Path, EC);
+    if (Iter != sys::fs::directory_iterator())
       CurrentEntry = directory_entry(Iter->path(), Iter->type());
   }
 
   std::error_code increment() override {
+    [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
     std::error_code EC;
     Iter.increment(EC);
     CurrentEntry = (Iter == llvm::sys::fs::directory_iterator())
@@ -436,6 +463,8 @@ class RealFSDirIter : public llvm::vfs::detail::DirIterImpl {
 
 directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
                                              std::error_code &EC) {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   SmallString<128> Storage;
   return directory_iterator(
       std::make_shared<RealFSDirIter>(adjustPath(Dir, Storage), EC));

>From 9588a13da463af8bf244fbc3258e0d4f6c8e0e74 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 16:42:35 -0700
Subject: [PATCH 07/19] [Support] Allow `LockFileManager` in IO sandbox

---
 llvm/lib/Support/LockFileManager.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/llvm/lib/Support/LockFileManager.cpp b/llvm/lib/Support/LockFileManager.cpp
index cdded51108b50..22c7de3b94b1d 100644
--- a/llvm/lib/Support/LockFileManager.cpp
+++ b/llvm/lib/Support/LockFileManager.cpp
@@ -14,6 +14,7 @@
 #include "llvm/Support/ErrorOr.h"
 #include "llvm/Support/ExponentialBackoff.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Process.h"
 #include "llvm/Support/Signals.h"
@@ -161,6 +162,8 @@ LockFileManager::LockFileManager(StringRef FileName)
     : FileName(FileName), Owner(OwnerUnknown{}) {}
 
 Expected<bool> LockFileManager::tryLock() {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   assert(std::holds_alternative<OwnerUnknown>(Owner) &&
          "lock has already been attempted");
 
@@ -246,6 +249,8 @@ Expected<bool> LockFileManager::tryLock() {
 }
 
 LockFileManager::~LockFileManager() {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   if (!std::holds_alternative<OwnedByUs>(Owner))
     return;
 
@@ -259,6 +264,8 @@ LockFileManager::~LockFileManager() {
 
 WaitForUnlockResult
 LockFileManager::waitForUnlockFor(std::chrono::seconds MaxSeconds) {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   auto *LockFileOwner = std::get_if<OwnedByAnother>(&Owner);
   assert(LockFileOwner &&
          "waiting for lock to be unlocked without knowing the owner");
@@ -288,5 +295,7 @@ LockFileManager::waitForUnlockFor(std::chrono::seconds MaxSeconds) {
 }
 
 std::error_code LockFileManager::unsafeMaybeUnlock() {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   return sys::fs::remove(LockFileName);
 }

>From 9f5e5c18597d1ccea39a8b09968155688c6d9a0f Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 16:43:22 -0700
Subject: [PATCH 08/19] [Support] Allow `raw_fd_ostream` in IO sandbox

---
 llvm/lib/Support/raw_ostream.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
index 07b99896543bd..e6ed38f29b83f 100644
--- a/llvm/lib/Support/raw_ostream.cpp
+++ b/llvm/lib/Support/raw_ostream.cpp
@@ -20,6 +20,7 @@
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/NativeFormatting.h"
 #include "llvm/Support/Process.h"
@@ -565,6 +566,9 @@ void format_object_base::home() {
 static int getFD(StringRef Filename, std::error_code &EC,
                  sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access,
                  sys::fs::OpenFlags Flags) {
+  // FIXME(sandboxing): Remove this by adopting `llvm::vfs::OutputBackend`.
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   assert((Access & sys::fs::FA_Write) &&
          "Cannot make a raw_ostream from a read-only descriptor!");
 
@@ -617,6 +621,9 @@ raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
 raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered,
                                OStreamKind K)
     : raw_pwrite_stream(unbuffered, K), FD(fd), ShouldClose(shouldClose) {
+  // FIXME(sandboxing): Remove this by adopting `llvm::vfs::OutputBackend`.
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   if (FD < 0 ) {
     ShouldClose = false;
     return;

>From 81111a2dd2cdd6b9b47359ee6faa5a4291864dcc Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 16:46:07 -0700
Subject: [PATCH 09/19] [Support] Allow `OnDiskOutputBackend` in IO sandbox

---
 llvm/lib/Support/VirtualOutputBackends.cpp | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/llvm/lib/Support/VirtualOutputBackends.cpp b/llvm/lib/Support/VirtualOutputBackends.cpp
index de59b8ab63a53..b78d54c06afa3 100644
--- a/llvm/lib/Support/VirtualOutputBackends.cpp
+++ b/llvm/lib/Support/VirtualOutputBackends.cpp
@@ -18,6 +18,7 @@
 #include "llvm/Support/VirtualOutputBackends.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/LockFileManager.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
@@ -255,6 +256,8 @@ static Error createDirectoriesOnDemand(StringRef OutputPath,
 }
 
 Error OnDiskOutputFile::tryToCreateTemporary(std::optional<int> &FD) {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   // Create a temporary file.
   // Insert -%%%%%%%% before the extension (if any), and because some tools
   // (noticeable, clang's own GlobalModuleIndex.cpp) glob for build
@@ -283,6 +286,8 @@ Error OnDiskOutputFile::tryToCreateTemporary(std::optional<int> &FD) {
 }
 
 Error OnDiskOutputFile::initializeFile(std::optional<int> &FD) {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   assert(OutputPath != "-" && "Unexpected request for FD of stdout");
 
   // Disable temporary file for other non-regular files, and if we get a status
@@ -331,6 +336,8 @@ Error OnDiskOutputFile::initializeFile(std::optional<int> &FD) {
 }
 
 Error OnDiskOutputFile::initializeStream() {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   // Open the file stream.
   if (OutputPath == "-") {
     std::error_code EC;
@@ -435,6 +442,8 @@ areFilesDifferent(const llvm::Twine &Source, const llvm::Twine &Destination) {
 }
 
 Error OnDiskOutputFile::reset() {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   // Destroy the streams to flush them.
   BufferOS.reset();
   if (!FileOS)
@@ -449,6 +458,8 @@ Error OnDiskOutputFile::reset() {
 }
 
 Error OnDiskOutputFile::keep() {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   if (auto E = reset())
     return E;
 
@@ -553,6 +564,8 @@ Error OnDiskOutputFile::keep() {
 }
 
 Error OnDiskOutputFile::discard() {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   // Destroy the streams to flush them.
   if (auto E = reset())
     return E;
@@ -575,6 +588,8 @@ Error OnDiskOutputFile::discard() {
 }
 
 Error OnDiskOutputBackend::makeAbsolute(SmallVectorImpl<char> &Path) const {
+  // FIXME: Should this really call sys::fs::make_absolute?
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
   return convertToOutputError(StringRef(Path.data(), Path.size()),
                               sys::fs::make_absolute(Path));
 }
@@ -582,6 +597,8 @@ Error OnDiskOutputBackend::makeAbsolute(SmallVectorImpl<char> &Path) const {
 Expected<std::unique_ptr<OutputFileImpl>>
 OnDiskOutputBackend::createFileImpl(StringRef Path,
                                     std::optional<OutputConfig> Config) {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+
   SmallString<256> AbsPath;
   if (Path != "-") {
     AbsPath = Path;

>From eea1b449ccf64149bd94af1459989018bca40520 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 16:50:09 -0700
Subject: [PATCH 10/19] [clang] Allow `ModuleCache` and `GlobalModuleIndex` in
 IO sandbox

---
 clang/lib/Serialization/GlobalModuleIndex.cpp |  7 +++++++
 clang/lib/Serialization/ModuleCache.cpp       | 19 +++++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/clang/lib/Serialization/GlobalModuleIndex.cpp b/clang/lib/Serialization/GlobalModuleIndex.cpp
index 1e2272c48bd04..297f5645b1645 100644
--- a/clang/lib/Serialization/GlobalModuleIndex.cpp
+++ b/clang/lib/Serialization/GlobalModuleIndex.cpp
@@ -24,6 +24,7 @@
 #include "llvm/Bitstream/BitstreamWriter.h"
 #include "llvm/Support/DJB.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/LockFileManager.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/OnDiskHashTable.h"
@@ -250,6 +251,9 @@ GlobalModuleIndex::~GlobalModuleIndex() {
 
 std::pair<GlobalModuleIndex *, llvm::Error>
 GlobalModuleIndex::readIndex(StringRef Path) {
+  // This is a compiler-internal input/output, let's bypass the sandbox.
+  [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+
   // Load the index file, if it's there.
   llvm::SmallString<128> IndexPath;
   IndexPath += Path;
@@ -843,6 +847,9 @@ llvm::Error
 GlobalModuleIndex::writeIndex(FileManager &FileMgr,
                               const PCHContainerReader &PCHContainerRdr,
                               StringRef Path) {
+  // This is a compiler-internal input/output, let's bypass the sandbox.
+  [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+
   llvm::SmallString<128> IndexPath;
   IndexPath += Path;
   llvm::sys::path::append(IndexPath, IndexFileName);
diff --git a/clang/lib/Serialization/ModuleCache.cpp b/clang/lib/Serialization/ModuleCache.cpp
index 9850956380423..fbe169db4accd 100644
--- a/clang/lib/Serialization/ModuleCache.cpp
+++ b/clang/lib/Serialization/ModuleCache.cpp
@@ -11,6 +11,7 @@
 #include "clang/Serialization/InMemoryModuleCache.h"
 #include "clang/Serialization/ModuleFile.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/LockFileManager.h"
 #include "llvm/Support/Path.h"
 
@@ -27,6 +28,9 @@ void clang::maybePruneImpl(StringRef Path, time_t PruneInterval,
   if (PruneInterval <= 0 || PruneAfter <= 0)
     return;
 
+  // This is a compiler-internal input/output, let's bypass the sandbox.
+  [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+
   llvm::SmallString<128> TimestampFile(Path);
   llvm::sys::path::append(TimestampFile, "modules.timestamp");
 
@@ -103,6 +107,9 @@ class CrossProcessModuleCache : public ModuleCache {
 
 public:
   void prepareForGetLock(StringRef ModuleFilename) override {
+    // This is a compiler-internal input/output, let's bypass the sandbox.
+    [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+
     // FIXME: Do this in LockFileManager and only if the directory doesn't
     // exist.
     StringRef Dir = llvm::sys::path::parent_path(ModuleFilename);
@@ -111,10 +118,16 @@ class CrossProcessModuleCache : public ModuleCache {
 
   std::unique_ptr<llvm::AdvisoryLock>
   getLock(StringRef ModuleFilename) override {
+    // This is a compiler-internal input/output, let's bypass the sandbox.
+    [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+
     return std::make_unique<llvm::LockFileManager>(ModuleFilename);
   }
 
   std::time_t getModuleTimestamp(StringRef ModuleFilename) override {
+    // This is a compiler-internal input/output, let's bypass the sandbox.
+    [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+
     std::string TimestampFilename =
         serialization::ModuleFile::getTimestampFilename(ModuleFilename);
     llvm::sys::fs::file_status Status;
@@ -124,6 +137,9 @@ class CrossProcessModuleCache : public ModuleCache {
   }
 
   void updateModuleTimestamp(StringRef ModuleFilename) override {
+    // This is a compiler-internal input/output, let's bypass the sandbox.
+    [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+
     // Overwrite the timestamp file contents so that file's mtime changes.
     std::error_code EC;
     llvm::raw_fd_ostream OS(
@@ -138,6 +154,9 @@ class CrossProcessModuleCache : public ModuleCache {
 
   void maybePrune(StringRef Path, time_t PruneInterval,
                   time_t PruneAfter) override {
+    // This is a compiler-internal input/output, let's bypass the sandbox.
+    [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+
     maybePruneImpl(Path, PruneInterval, PruneAfter);
   }
 

>From e1aeec7e5c2b74f0a2bb3599abc1b4bb31861d3f Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 16:50:33 -0700
Subject: [PATCH 11/19] [clang] Allow `HTMLDiagnostics` in IO sandbox

---
 clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index 217b853305ed1..b201b644359e9 100644
--- a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -37,6 +37,7 @@
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cassert>
@@ -257,6 +258,9 @@ void HTMLDiagnostics::FlushDiagnosticsImpl(
 
 void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
                                  FilesMade *filesMade) {
+  // FIXME(sandboxing): Remove this by adopting `llvm::vfs::OutputBackend`.
+  [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+
   // Create the HTML directory if it is missing.
   if (!createdDir) {
     createdDir = true;

>From 48414bb98b370a1e0855a709498f1c864085ebd1 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 16:50:47 -0700
Subject: [PATCH 12/19] [clang] Allow ThinLTO indexing in IO sandbox

---
 clang/lib/CodeGen/BackendUtil.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index aefc262dca17f..e99e54f50d604 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -46,6 +46,7 @@
 #include "llvm/Support/BuryPointer.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Compiler.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/Program.h"
@@ -1434,6 +1435,8 @@ void clang::emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts,
 
   std::unique_ptr<llvm::Module> EmptyModule;
   if (!CGOpts.ThinLTOIndexFile.empty()) {
+    // FIXME(sandboxing): Figure out how to support distributed indexing.
+    [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
     // If we are performing a ThinLTO importing compile, load the function index
     // into memory and pass it into runThinLTOBackend, which will run the
     // function importer and invoke LTO passes.

>From dc64a2baa2256e26ebae2ae8151c8f2bd6d54002 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 16:51:03 -0700
Subject: [PATCH 13/19] [clang] Allow crash diagnostic file in IO sandbox

---
 clang/lib/Driver/Driver.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 40ea513e85427..ede5fb0e94423 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -87,6 +87,7 @@
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FileUtilities.h"
 #include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/MD5.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/PrettyStackTrace.h"
@@ -1870,6 +1871,8 @@ bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename,
   using namespace llvm::sys;
   assert(llvm::Triple(llvm::sys::getProcessTriple()).isOSDarwin() &&
          "Only knows about .crash files on Darwin");
+  // This is not a formal output of the compiler, let's bypass the sandbox.
+  [[maybe_unused]] auto BypassSandbox = sandbox::scopedDisable();
 
   // The .crash file can be found on at ~/Library/Logs/DiagnosticReports/
   // (or /Library/Logs/DiagnosticReports for root) and has the filename pattern

>From 39ab9d5c1f17a9a0117d64a7c63db7d656236b34 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 19:55:02 -0700
Subject: [PATCH 14/19] [llvm] Allow `AsmPrinter` in IO sandbox

---
 llvm/include/llvm/CodeGen/AsmPrinter.h              | 8 --------
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp          | 1 -
 llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp | 6 +++++-
 3 files changed, 5 insertions(+), 10 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 9ace2555b4b62..19ca44429af4d 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -16,7 +16,6 @@
 #define LLVM_CODEGEN_ASMPRINTER_H
 
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallVector.h"
@@ -88,10 +87,6 @@ namespace remarks {
 class RemarkStreamer;
 }
 
-namespace vfs {
-class FileSystem;
-}
-
 /// This class is intended to be used as a driving class for all asm writers.
 class LLVM_ABI AsmPrinter : public MachineFunctionPass {
 public:
@@ -110,9 +105,6 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   /// generating (such as the current section etc).
   std::unique_ptr<MCStreamer> OutStreamer;
 
-  /// The VFS to resolve asm include directives.
-  IntrusiveRefCntPtr<vfs::FileSystem> VFS;
-
   /// The current machine function.
   MachineFunction *MF = nullptr;
 
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 8aa488f0efd8f..ceba3833c7dd2 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -482,7 +482,6 @@ void AsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const {
 }
 
 bool AsmPrinter::doInitialization(Module &M) {
-  VFS = vfs::getRealFileSystem();
   auto *MMIWP = getAnalysisIfAvailable<MachineModuleInfoWrapperPass>();
   MMI = MMIWP ? &MMIWP->getMMI() : nullptr;
   HasSplitStack = false;
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
index 8dd8b9da9c50c..4d01e397af5c0 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
@@ -99,7 +99,11 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
   unsigned BufNum = addInlineAsmDiagBuffer(Str, LocMDNode);
   SourceMgr &SrcMgr = *MMI->getContext().getInlineSourceManager();
   SrcMgr.setIncludeDirs(MCOptions.IASSearchPaths);
-  SrcMgr.setVirtualFileSystem(VFS);
+  SrcMgr.setVirtualFileSystem([] {
+    // FIXME(sandboxing): Propagating vfs::FileSystem here is lots of work.
+    [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+    return vfs::getRealFileSystem();
+  }());
 
   std::unique_ptr<MCAsmParser> Parser(
       createMCAsmParser(SrcMgr, OutContext, *OutStreamer, *MAI, BufNum));

>From 1f7011925264fbf2d4d7d2bc8d0f77a71ac3e216 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 19:55:17 -0700
Subject: [PATCH 15/19] [llvm] Allow `BTFDebug` in IO sandbox

---
 llvm/lib/Target/BPF/BTFDebug.cpp | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp
index a652b7e9c537f..71cfdf1e98438 100644
--- a/llvm/lib/Target/BPF/BTFDebug.cpp
+++ b/llvm/lib/Target/BPF/BTFDebug.cpp
@@ -25,6 +25,7 @@
 #include "llvm/MC/MCSectionELF.h"
 #include "llvm/MC/MCStreamer.h"
 #include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/LineIterator.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Target/TargetLoweringObjectFile.h"
@@ -1108,12 +1109,17 @@ std::string BTFDebug::populateFileContent(const DIFile *File) {
   std::string Line;
   Content.push_back(Line); // Line 0 for empty string
 
+  auto LoadFile = [](StringRef FileName) {
+    // FIXME(sandboxing): Propagating vfs::FileSystem here is lots of work.
+    [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
+    return MemoryBuffer::getFile(FileName);
+  };
+
   std::unique_ptr<MemoryBuffer> Buf;
   auto Source = File->getSource();
   if (Source)
     Buf = MemoryBuffer::getMemBufferCopy(*Source);
-  else if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
-               MemoryBuffer::getFile(FileName))
+  else if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = LoadFile(FileName))
     Buf = std::move(*BufOrErr);
   if (Buf)
     for (line_iterator I(*Buf, false), E; I != E; ++I)

>From 96e703d63274a228a991b8a347f9441cc4be00a3 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 19:55:47 -0700
Subject: [PATCH 16/19] [clang] Allow `FileManager` to read stdin

---
 clang/lib/Basic/FileManager.cpp | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Basic/FileManager.cpp b/clang/lib/Basic/FileManager.cpp
index e744cc0afcded..f1fc453be898a 100644
--- a/clang/lib/Basic/FileManager.cpp
+++ b/clang/lib/Basic/FileManager.cpp
@@ -347,12 +347,15 @@ llvm::Expected<FileEntryRef> FileManager::getSTDIN() {
   if (STDIN)
     return *STDIN;
 
-  std::unique_ptr<llvm::MemoryBuffer> Content;
-  if (auto ContentOrError = llvm::MemoryBuffer::getSTDIN())
-    Content = std::move(*ContentOrError);
-  else
+  auto ContentOrError = [] {
+    [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+    return llvm::MemoryBuffer::getSTDIN();
+  }();
+
+  if (!ContentOrError)
     return llvm::errorCodeToError(ContentOrError.getError());
 
+  auto Content = std::move(*ContentOrError);
   STDIN = getVirtualFileRef(Content->getBufferIdentifier(),
                             Content->getBufferSize(), 0);
   FileEntry &FE = const_cast<FileEntry &>(STDIN->getFileEntry());

>From f96829ebf211c6ef0cbd9f10f20a6082fa30935b Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 27 Oct 2025 19:56:22 -0700
Subject: [PATCH 17/19] [clang] Use the VFS to make prebuilt module paths
 absolute

---
 clang/lib/Lex/HeaderSearch.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp
index 65c324c10ca5d..02b34b10375a3 100644
--- a/clang/lib/Lex/HeaderSearch.cpp
+++ b/clang/lib/Lex/HeaderSearch.cpp
@@ -221,7 +221,7 @@ std::string HeaderSearch::getPrebuiltModuleFileName(StringRef ModuleName,
   // file.
   for (const std::string &Dir : HSOpts.PrebuiltModulePaths) {
     SmallString<256> Result(Dir);
-    llvm::sys::fs::make_absolute(Result);
+    FileMgr.makeAbsolutePath(Result);
     if (ModuleName.contains(':'))
       // The separator of C++20 modules partitions (':') is not good for file
       // systems, here clang and gcc choose '-' by default since it is not a

>From d4a425a0dae4036bf6a2aa5033bef46b28036e36 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Tue, 28 Oct 2025 06:56:00 -0700
Subject: [PATCH 18/19] Squash with "[clang] Enable IO sandboxing in -cc1 and
 -cc1as"

---
 llvm/lib/Support/Unix/Signals.inc    | 1 +
 llvm/lib/Support/Windows/Signals.inc | 4 +++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc
index 78d6540db98a7..3b73897777402 100644
--- a/llvm/lib/Support/Unix/Signals.inc
+++ b/llvm/lib/Support/Unix/Signals.inc
@@ -354,6 +354,7 @@ void sys::unregisterHandlers() {
 
 /// Process the FilesToRemove list.
 static void RemoveFilesToRemove() {
+  [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
   FileToRemoveList::removeAllFiles(FilesToRemove);
 }
 
diff --git a/llvm/lib/Support/Windows/Signals.inc b/llvm/lib/Support/Windows/Signals.inc
index 648d6a50287ec..c75bb966e0957 100644
--- a/llvm/lib/Support/Windows/Signals.inc
+++ b/llvm/lib/Support/Windows/Signals.inc
@@ -563,11 +563,13 @@ static void Cleanup(bool ExecuteSignalHandlers) {
   CleanupExecuted = true;
 
   // FIXME: open files cannot be deleted.
-  if (FilesToRemove != NULL)
+  if (FilesToRemove != NULL) {
+    [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable();
     while (!FilesToRemove->empty()) {
       llvm::sys::fs::remove(FilesToRemove->back());
       FilesToRemove->pop_back();
     }
+  }
 
   if (ExecuteSignalHandlers)
     llvm::sys::RunSignalHandlers();

>From fbb67e6e7c83ae735d97b0122fdba17be2da4ceb Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Thu, 30 Oct 2025 15:53:00 -0700
Subject: [PATCH 19/19] Windows CI fix?

---
 llvm/lib/Support/Unix/Signals.inc    | 2 ++
 llvm/lib/Support/Windows/Signals.inc | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc
index 3b73897777402..770fd701c1b9f 100644
--- a/llvm/lib/Support/Unix/Signals.inc
+++ b/llvm/lib/Support/Unix/Signals.inc
@@ -359,6 +359,8 @@ static void RemoveFilesToRemove() {
 }
 
 void sys::CleanupOnSignal(uintptr_t Context) {
+  [[maybe_unused]] auto BypassSandbox = sandbox::scopedDisable();
+
   int Sig = (int)Context;
 
   if (llvm::is_contained(InfoSigs, Sig)) {
diff --git a/llvm/lib/Support/Windows/Signals.inc b/llvm/lib/Support/Windows/Signals.inc
index c75bb966e0957..9910f9dd9c86d 100644
--- a/llvm/lib/Support/Windows/Signals.inc
+++ b/llvm/lib/Support/Windows/Signals.inc
@@ -778,6 +778,8 @@ WriteWindowsDumpFile(PMINIDUMP_EXCEPTION_INFORMATION ExceptionInfo) {
 }
 
 void sys::CleanupOnSignal(uintptr_t Context) {
+  [[maybe_unused]] auto BypassSandbox = sandbox::scopedDisable();
+
   LPEXCEPTION_POINTERS EP = (LPEXCEPTION_POINTERS)Context;
   // Broken pipe is not a crash.
   //



More information about the llvm-commits mailing list