https://github.com/jansvoboda11 updated https://github.com/llvm/llvm-project/pull/165350
>From db5971beb9a4108b300e4dd666d3b6760d4c4890 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 16:00:53 -0700 Subject: [PATCH 01/20] [Support] Introduce IO sandboxing --- llvm/CMakeLists.txt | 1 + llvm/include/llvm/Config/llvm-config.h.cmake | 3 ++ llvm/include/llvm/Support/IOSandbox.h | 41 ++++++++++++++++++++ llvm/lib/Support/Signals.cpp | 4 ++ llvm/lib/Support/Unix/Signals.inc | 10 ++++- llvm/lib/Support/Windows/Signals.inc | 6 +++ 6 files changed, 64 insertions(+), 1 deletion(-) 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..3a0ded6f4ab29 --- /dev/null +++ b/llvm/include/llvm/Support/IOSandbox.h @@ -0,0 +1,41 @@ +//===- 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 + +#if defined(LLVM_ENABLE_IO_SANDBOX) && LLVM_ENABLE_IO_SANDBOX + +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/SaveAndRestore.h" + +namespace llvm::sys::sandbox { +inline thread_local bool Enabled = false; +struct ScopedSetting { + SaveAndRestore<bool> Impl; +}; +inline ScopedSetting scopedEnable() { return {{Enabled, true}}; } +inline ScopedSetting scopedDisable() { return {{Enabled, false}}; } +inline void violationIfEnabled() { + if (Enabled) + reportFatalInternalError("IO sandbox violation"); +} +} // namespace llvm::sys::sandbox + +#else + +namespace llvm::sys::sandbox { +struct [[maybe_unused]] ScopedSetting {}; +inline ScopedSetting scopedEnable() { return {}; } +inline ScopedSetting scopedDisable() { return {}; } +inline void violationIfEnabled() {} +} // namespace llvm::sys::sandbox + +#endif + +#endif diff --git a/llvm/lib/Support/Signals.cpp b/llvm/lib/Support/Signals.cpp index f8a14a45ddc3e..f160a135f623d 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. + auto BypassSandbox = sandbox::scopedDisable(); + for (CallbackAndCookie &RunMe : CallBacksToRun()) { auto Expected = CallbackAndCookie::Status::Initialized; auto Desired = CallbackAndCookie::Status::Executing; diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc index 78d6540db98a7..56ad4fc504153 100644 --- a/llvm/lib/Support/Unix/Signals.inc +++ b/llvm/lib/Support/Unix/Signals.inc @@ -358,6 +358,9 @@ static void RemoveFilesToRemove() { } void sys::CleanupOnSignal(uintptr_t Context) { + // Let's not interfere with stack trace symbolication and friends. + auto BypassSandbox = sandbox::scopedDisable(); + int Sig = (int)Context; if (llvm::is_contained(InfoSigs, Sig)) { @@ -440,7 +443,12 @@ static void InfoSignalHandler(int Sig) { CurrentInfoFunction(); } -void llvm::sys::RunInterruptHandlers() { RemoveFilesToRemove(); } +void sys::RunInterruptHandlers() { + // Let's not interfere with stack trace symbolication and friends. + auto BypassSandbox = sandbox::scopedDisable(); + + RemoveFilesToRemove(); +} void llvm::sys::SetInterruptFunction(void (*IF)()) { InterruptFunction.exchange(IF); diff --git a/llvm/lib/Support/Windows/Signals.inc b/llvm/lib/Support/Windows/Signals.inc index 648d6a50287ec..b1fd682fc665d 100644 --- a/llvm/lib/Support/Windows/Signals.inc +++ b/llvm/lib/Support/Windows/Signals.inc @@ -576,6 +576,9 @@ static void Cleanup(bool ExecuteSignalHandlers) { } void llvm::sys::RunInterruptHandlers() { + // Let's not interfere with stack trace symbolication and friends. + auto BypassSandbox = sandbox::scopedDisable(); + // The interrupt handler may be called from an interrupt, but it may also be // called manually (such as the case of report_fatal_error with no registered // error handler). We must ensure that the critical section is properly @@ -776,6 +779,9 @@ WriteWindowsDumpFile(PMINIDUMP_EXCEPTION_INFORMATION ExceptionInfo) { } void sys::CleanupOnSignal(uintptr_t Context) { + // Let's not interfere with stack trace symbolication and friends. + auto BypassSandbox = sandbox::scopedDisable(); + LPEXCEPTION_POINTERS EP = (LPEXCEPTION_POINTERS)Context; // Broken pipe is not a crash. // >From bafb88903bc797213d926c2b720870a5d0d3edf4 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 16:01:48 -0700 Subject: [PATCH 02/20] [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 ++++++++++--- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index 715429bcd2096..da7a1f2e07e90 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. + 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..69264c0cd83aa 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 = [] { + 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. + 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..4f8739cc21748 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. + 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 = [] { + auto BypassSandbox = sys::sandbox::scopedDisable(); + return vfs::getRealFileSystem(); + }(); // Set an error handler, so that any LLVM backend diagnostics go through our // error handler. >From a2e9ca84392484390c6e76a66850129e20ecf110 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 16:09:57 -0700 Subject: [PATCH 03/20] [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 23b9f8c5790d2..5e084e516a943 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 18d626f5f00b8e0d83bfc95cc5bb49dd1eafc2bb Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 16:12:29 -0700 Subject: [PATCH 04/20] [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 77c35cbe47efeb423e2a2707c6c2daeb92910f50 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 16:35:26 -0700 Subject: [PATCH 05/20] [Support] Disallow input `sys::fs` APIs in IO sandbox --- llvm/include/llvm/Support/FileSystem.h | 11 +++++++ llvm/lib/Support/Path.cpp | 21 ++++++++++++++ llvm/lib/Support/Unix/Path.inc | 37 ++++++++++++++++++++++++ llvm/lib/Support/Windows/Path.inc | 40 ++++++++++++++++++++++++-- 4 files changed, 106 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..a27656022b440 100644 --- a/llvm/lib/Support/Path.cpp +++ b/llvm/lib/Support/Path.cpp @@ -19,6 +19,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/IOSandbox.h" #include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" #include <cctype> @@ -834,6 +835,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 +956,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 +1050,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 +1072,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 +1103,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 +1117,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 +1131,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 +1147,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 +1166,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 +1192,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 40f43070bd12dcec9d8cd25a9f06605d45bcf820 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 16:40:27 -0700 Subject: [PATCH 06/20] [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..72dc49db565cd 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() { + 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) { + 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() { + auto BypassSandbox = sys::sandbox::scopedDisable(); + std::error_code EC = sys::fs::closeFile(FD); FD = kInvalidFile; return EC; } void RealFile::setPath(const Twine &Path) { + 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) { + 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) { + auto BypassSandbox = sys::sandbox::scopedDisable(); + return openFileForReadWithFlags(Name, sys::fs::OF_Text); } ErrorOr<std::unique_ptr<File>> RealFileSystem::openFileForReadBinary(const Twine &Name) { + auto BypassSandbox = sys::sandbox::scopedDisable(); + return openFileForReadWithFlags(Name, sys::fs::OF_None); } llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const { + 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) { + 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) { + 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) { + 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) { + 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 { + 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) { + auto BypassSandbox = sys::sandbox::scopedDisable(); + SmallString<128> Storage; return directory_iterator( std::make_shared<RealFSDirIter>(adjustPath(Dir, Storage), EC)); >From 24493d747cfbe3a50154a051a9eb5a6282114cff Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 16:42:35 -0700 Subject: [PATCH 07/20] [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..0891ef980668b 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() { + 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() { + 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) { + 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() { + auto BypassSandbox = sys::sandbox::scopedDisable(); + return sys::fs::remove(LockFileName); } >From 7af44dbcf35af3d30588e476a4a94757112315eb Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 16:43:22 -0700 Subject: [PATCH 08/20] [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..05de780c41b44 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`. + 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`. + auto BypassSandbox = sys::sandbox::scopedDisable(); + if (FD < 0 ) { ShouldClose = false; return; >From 5b2f16027df4d0acb5e5b13301987cc3b447bdc3 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 16:46:07 -0700 Subject: [PATCH 09/20] [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..739910c15fbea 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) { + 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) { + 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() { + 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() { + 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() { + auto BypassSandbox = sys::sandbox::scopedDisable(); + if (auto E = reset()) return E; @@ -553,6 +564,8 @@ Error OnDiskOutputFile::keep() { } Error OnDiskOutputFile::discard() { + 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? + 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) { + auto BypassSandbox = sys::sandbox::scopedDisable(); + SmallString<256> AbsPath; if (Path != "-") { AbsPath = Path; >From a8162da38a29a29bd91536d78f60e49b806b44ab Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 16:50:09 -0700 Subject: [PATCH 10/20] [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..2246a3ac0a57e 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. + 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. + 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..6837807444ce4 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. + 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. + 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. + 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. + 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. + 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. + auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + maybePruneImpl(Path, PruneInterval, PruneAfter); } >From ad82967ced0987a48e48b43ae983ac387e46b180 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 16:50:33 -0700 Subject: [PATCH 11/20] [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..77c1cceb60fe2 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`. + auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + // Create the HTML directory if it is missing. if (!createdDir) { createdDir = true; >From 56fda7e2e52e5d0b4effbcf773640219842eb3d1 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 16:50:47 -0700 Subject: [PATCH 12/20] [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 3c313149ca1fc..609dab29b1e9d 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" @@ -1442,6 +1443,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. + 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 92d60bb681951e9acf34c26925a277b8e295c406 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 16:51:03 -0700 Subject: [PATCH 13/20] [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 71c52807091ba..255dc18dd2c39 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" @@ -1879,6 +1880,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. + 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 37afa44fad930a43822cc22003111665689d903e Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 19:55:02 -0700 Subject: [PATCH 14/20] [CodeGen] 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 | 7 ++++++- 3 files changed, 6 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 f65d88a669f13..306324860ce34 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..69c2950711c7c 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp @@ -34,6 +34,7 @@ #include "llvm/MC/MCSymbol.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/IOSandbox.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/VirtualFileSystem.h" @@ -99,7 +100,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. + auto BypassSandbox = sys::sandbox::scopedDisable(); + return vfs::getRealFileSystem(); + }()); std::unique_ptr<MCAsmParser> Parser( createMCAsmParser(SrcMgr, OutContext, *OutStreamer, *MAI, BufNum)); >From 506bc478f3d1701fc2b607affe71aa6de8ab0263 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 19:55:17 -0700 Subject: [PATCH 15/20] [Target] 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..fb90a2ca5b383 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. + 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 b6b973956f81862669acd885fc94355925605bb1 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Mon, 27 Oct 2025 19:55:47 -0700 Subject: [PATCH 16/20] [clang] Allow `FileManager` to read stdin --- clang/lib/Basic/FileManager.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/clang/lib/Basic/FileManager.cpp b/clang/lib/Basic/FileManager.cpp index e744cc0afcded..6d6ea5b84369b 100644 --- a/clang/lib/Basic/FileManager.cpp +++ b/clang/lib/Basic/FileManager.cpp @@ -22,6 +22,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/IOSandbox.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -347,12 +348,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 = [] { + 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 8e4947618453845e14779038575f43811f11af11 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Fri, 31 Oct 2025 09:55:00 -0700 Subject: [PATCH 17/20] [REMOVE] Force-enable IO sandboxing --- llvm/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt index eba10b7a703dd..3e926008b2670 100644 --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -704,7 +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_IO_SANDBOX "Enable IO sandboxing in supported tools" ON) option(LLVM_ENABLE_EXPENSIVE_CHECKS "Enable expensive checks" OFF) set(LLVM_ABI_BREAKING_CHECKS "WITH_ASSERTS" CACHE STRING >From 276dd0853bc0728904a539e0144a4fbdcd71f055 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Fri, 7 Nov 2025 16:07:03 -0800 Subject: [PATCH 18/20] Make use of `LLVM_THREAD_LOCAL` --- llvm/include/llvm/Support/IOSandbox.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/include/llvm/Support/IOSandbox.h b/llvm/include/llvm/Support/IOSandbox.h index 3a0ded6f4ab29..3a68f67ec20eb 100644 --- a/llvm/include/llvm/Support/IOSandbox.h +++ b/llvm/include/llvm/Support/IOSandbox.h @@ -11,11 +11,12 @@ #if defined(LLVM_ENABLE_IO_SANDBOX) && LLVM_ENABLE_IO_SANDBOX +#include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SaveAndRestore.h" namespace llvm::sys::sandbox { -inline thread_local bool Enabled = false; +inline LLVM_THREAD_LOCAL bool Enabled = false; struct ScopedSetting { SaveAndRestore<bool> Impl; }; >From 89298503d583e3e06aed2ae889e8d01635cc3a50 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Fri, 7 Nov 2025 16:07:42 -0800 Subject: [PATCH 19/20] Drop bypass from `CrossProcessModuleCache::getLock()` --- clang/lib/Serialization/ModuleCache.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/clang/lib/Serialization/ModuleCache.cpp b/clang/lib/Serialization/ModuleCache.cpp index 6837807444ce4..e6e8cbf171bfa 100644 --- a/clang/lib/Serialization/ModuleCache.cpp +++ b/clang/lib/Serialization/ModuleCache.cpp @@ -118,9 +118,6 @@ 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. - auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); - return std::make_unique<llvm::LockFileManager>(ModuleFilename); } >From b71e69571eb145daf48b2c1b9234b472bff436f0 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Fri, 7 Nov 2025 16:16:13 -0800 Subject: [PATCH 20/20] Sink `directory_iterator` violations from header into the implementations --- llvm/include/llvm/Support/FileSystem.h | 11 ----------- llvm/lib/Support/MemoryBuffer.cpp | 1 + llvm/lib/Support/Unix/Path.inc | 7 +++++++ llvm/lib/Support/Windows/Path.inc | 5 +++++ 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/llvm/include/llvm/Support/FileSystem.h b/llvm/include/llvm/Support/FileSystem.h index fa8753d146e22..cf2a8104ac813 100644 --- a/llvm/include/llvm/Support/FileSystem.h +++ b/llvm/include/llvm/Support/FileSystem.h @@ -36,7 +36,6 @@ #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> @@ -1448,8 +1447,6 @@ 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( @@ -1459,8 +1456,6 @@ 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); @@ -1471,8 +1466,6 @@ 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; } @@ -1518,8 +1511,6 @@ 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(); @@ -1527,8 +1518,6 @@ 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/MemoryBuffer.cpp b/llvm/lib/Support/MemoryBuffer.cpp index 5e084e516a943..6ee2d05eaafd0 100644 --- a/llvm/lib/Support/MemoryBuffer.cpp +++ b/llvm/lib/Support/MemoryBuffer.cpp @@ -20,6 +20,7 @@ #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/IOSandbox.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/SmallVectorMemoryBuffer.h" diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc index ab4d2e33213a5..c199d313a262e 100644 --- a/llvm/lib/Support/Unix/Path.inc +++ b/llvm/lib/Support/Unix/Path.inc @@ -16,6 +16,9 @@ //===----------------------------------------------------------------------===// #include "Unix.h" + +#include "llvm/Support/IOSandbox.h" + #include <limits.h> #include <stdio.h> #include <sys/stat.h> @@ -925,6 +928,8 @@ int mapped_file_region::alignment() { return Process::getPageSizeEstimate(); } std::error_code detail::directory_iterator_construct(detail::DirIterState &it, StringRef path, bool follow_symlinks) { + sandbox::violationIfEnabled(); + SmallString<128> path_null(path); DIR *directory = ::opendir(path_null.c_str()); if (!directory) @@ -960,6 +965,8 @@ static file_type direntType(dirent *Entry) { } std::error_code detail::directory_iterator_increment(detail::DirIterState &It) { + sandbox::violationIfEnabled(); + errno = 0; dirent *CurDir = ::readdir(reinterpret_cast<DIR *>(It.IterationHandle)); if (CurDir == nullptr && errno != 0) { diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc index f848aec030cd6..9ea9619ca000d 100644 --- a/llvm/lib/Support/Windows/Path.inc +++ b/llvm/lib/Support/Windows/Path.inc @@ -17,6 +17,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/IOSandbox.h" #include "llvm/Support/WindowsError.h" #include <fcntl.h> #include <sys/stat.h> @@ -1070,6 +1071,8 @@ static basic_file_status status_from_find_data(WIN32_FIND_DATAW *FindData) { std::error_code detail::directory_iterator_construct(detail::DirIterState &IT, StringRef Path, bool FollowSymlinks) { + sandbox::violationIfEnabled(); + SmallVector<wchar_t, 128> PathUTF16; if (std::error_code EC = widenPath(Path, PathUTF16)) @@ -1135,6 +1138,8 @@ std::error_code detail::directory_iterator_destruct(detail::DirIterState &IT) { } std::error_code detail::directory_iterator_increment(detail::DirIterState &IT) { + sandbox::violationIfEnabled(); + WIN32_FIND_DATAW FindData; if (!::FindNextFileW(HANDLE(IT.IterationHandle), &FindData)) { DWORD LastError = ::GetLastError(); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
