From 0e29083706a92cb06f2a3fa564390dbb799104ee Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Wed, 16 Jul 2025 11:24:41 -0700 Subject: [PATCH 01/20] [llvm][clang] Sandbox IO operations (infrastructure) --- clang/lib/Driver/Job.cpp | 5 ++++ llvm/include/llvm/Support/IOSandbox.h | 20 +++++++++++++++ llvm/lib/Support/IOSandbox.cpp | 19 ++++++++++++++ llvm/lib/Support/IOSandboxInternal.h | 36 +++++++++++++++++++++++++++ llvm/lib/Support/Signals.cpp | 4 +++ 5 files changed, 84 insertions(+) create mode 100644 llvm/include/llvm/Support/IOSandbox.h create mode 100644 llvm/lib/Support/IOSandbox.cpp create mode 100644 llvm/lib/Support/IOSandboxInternal.h diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index 715429bcd2096..ab6cd2973dd6e 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> 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_scoped_enable(); + llvm::CrashRecoveryContext CRC; CRC.DumpStackAndCleanupOnFailure = true; diff --git a/llvm/include/llvm/Support/IOSandbox.h b/llvm/include/llvm/Support/IOSandbox.h new file mode 100644 index 0000000000000..1a8fb9788e3f6 --- /dev/null +++ b/llvm/include/llvm/Support/IOSandbox.h @@ -0,0 +1,20 @@ +//===- 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 + +#include "llvm/Support/SaveAndRestore.h" + +namespace llvm::sys { +SaveAndRestore sandbox_scoped_enable(); +SaveAndRestore sandbox_scoped_disable(); +void sandbox_violation_if_enabled(); +} // namespace llvm::sys + +#endif diff --git a/llvm/lib/Support/IOSandbox.cpp b/llvm/lib/Support/IOSandbox.cpp new file mode 100644 index 0000000000000..a046d38b48fc3 --- /dev/null +++ b/llvm/lib/Support/IOSandbox.cpp @@ -0,0 +1,19 @@ +#include "llvm/Support/IOSandbox.h" + +#include + +using namespace llvm; + +thread_local bool IOSandboxEnabled = false; + +SaveAndRestore sys::sandbox_scoped_enable() { + return {IOSandboxEnabled, true}; +} + +SaveAndRestore sys::sandbox_scoped_disable() { + return {IOSandboxEnabled, false}; +} + +void sys::sandbox_violation_if_enabled() { + assert(!IOSandboxEnabled && "sandbox violation"); +} diff --git a/llvm/lib/Support/IOSandboxInternal.h b/llvm/lib/Support/IOSandboxInternal.h new file mode 100644 index 0000000000000..184b220417fff --- /dev/null +++ b/llvm/lib/Support/IOSandboxInternal.h @@ -0,0 +1,36 @@ +#ifndef LLVM_SUPPORT_IOSANDBOXINTERNAL_H +#define LLVM_SUPPORT_IOSANDBOXINTERNAL_H + +#include "llvm/Support/IOSandbox.h" + +namespace llvm { +namespace detail { +template struct Interposed; + +template struct Interposed { + RetTy (*Fn)(ArgTy...); + + RetTy operator()(ArgTy... Arg) const { + sys::sandbox_violation_if_enabled(); + return Fn(std::forward(Arg)...); + } +}; + +template +struct Interposed { + RetTy (*Fn)(ArgTy..., ...); + + template + RetTy operator()(ArgTy... Arg, CVarArgTy... CVarArg) const { + sys::sandbox_violation_if_enabled(); + return Fn(std::forward(Arg)..., std::forward(CVarArg)...); + } +}; + +template constexpr auto interpose(FnTy Fn) { + return Interposed{Fn}; +} +} // namespace detail +} // namespace llvm + +#endif diff --git a/llvm/lib/Support/Signals.cpp b/llvm/lib/Support/Signals.cpp index f8a14a45ddc3e..076ec293750af 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_scoped_disable(); + for (CallbackAndCookie &RunMe : CallBacksToRun()) { auto Expected = CallbackAndCookie::Status::Initialized; auto Desired = CallbackAndCookie::Status::Executing; From be3530feb1f1d6f2798fae379a322ee77d09c566 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Wed, 16 Jul 2025 11:20:25 -0700 Subject: [PATCH 02/20] [llvm][clang] Sandbox IO operations (getting `vfs::RealFileSystem`) --- clang/lib/CodeGen/BackendUtil.cpp | 3 +++ clang/tools/driver/cc1_main.cpp | 7 ++++++- clang/tools/driver/cc1as_main.cpp | 6 +++++- llvm/lib/Support/VirtualFileSystem.cpp | 3 +++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 602068436101b..3066bd8313777 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 EmptyModule; if (!CGOpts.ThinLTOIndexFile.empty()) { + // FIXME(sandboxing): Figure out how to support distributed indexing. + auto BypassSandbox = sys::sandbox_scoped_disable(); // 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. diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp index 49f8843515a35..f8588d0ac4549 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 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_scoped_disable(); + return llvm::vfs::getRealFileSystem(); + }(); + Clang->createVirtualFileSystem(std::move(VFS), DiagsBuffer); // Create the actual diagnostics engine. Clang->createDiagnostics(); diff --git a/clang/tools/driver/cc1as_main.cpp b/clang/tools/driver/cc1as_main.cpp index 50da2f8449a22..4210267664f5b 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" @@ -672,7 +673,10 @@ int cc1as_main(ArrayRef 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_scoped_disable(); + return vfs::getRealFileSystem(); + }(); // Set an error handler, so that any LLVM backend diagnostics go through our // error handler. diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp index c754b30d8de4a..1d27d02a27f47 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,12 @@ void RealFileSystem::printImpl(raw_ostream &OS, PrintType Type, IntrusiveRefCntPtr vfs::getRealFileSystem() { static IntrusiveRefCntPtr FS = makeIntrusiveRefCnt(true); + sys::sandbox_violation_if_enabled(); return FS; } std::unique_ptr vfs::createPhysicalFileSystem() { + sys::sandbox_violation_if_enabled(); return std::make_unique(false); } From 9c944d688bab8e8ca3f776547021b1f564e8b00c Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Thu, 10 Jul 2025 08:01:31 -0700 Subject: [PATCH 03/20] [llvm][clang] Sandbox IO operations (read, pread) --- clang/lib/Serialization/GlobalModuleIndex.cpp | 4 ++ clang/tools/driver/cc1_main.cpp | 20 +++--- clang/tools/driver/cc1as_main.cpp | 7 +- llvm/lib/Support/IOSandboxInternal.h | 10 +++ llvm/lib/Support/Unix/Path.inc | 68 ++++++++++--------- llvm/lib/Support/VirtualFileSystem.cpp | 11 ++- 6 files changed, 76 insertions(+), 44 deletions(-) diff --git a/clang/lib/Serialization/GlobalModuleIndex.cpp b/clang/lib/Serialization/GlobalModuleIndex.cpp index 1e2272c48bd04..613121a9596fd 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::readIndex(StringRef Path) { + // This is a compiler-internal input/output, let's bypass the sandbox. + auto BypassSandbox = llvm::sys::sandbox_scoped_disable(); + // Load the index file, if it's there. llvm::SmallString<128> IndexPath; IndexPath += Path; diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp index f8588d0ac4549..af30ba68dfde4 100644 --- a/clang/tools/driver/cc1_main.cpp +++ b/clang/tools/driver/cc1_main.cpp @@ -307,15 +307,19 @@ int cc1_main(ArrayRef 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 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_scoped_disable(); + std::unique_ptr 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()) { // It is possible that the compiler instance doesn't own a file manager here diff --git a/clang/tools/driver/cc1as_main.cpp b/clang/tools/driver/cc1as_main.cpp index 4210267664f5b..285a128add1e1 100644 --- a/clang/tools/driver/cc1as_main.cpp +++ b/clang/tools/driver/cc1as_main.cpp @@ -426,8 +426,11 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts, if (!TheTarget) return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple.str(); - ErrorOr> Buffer = - MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true); + ErrorOr> Buffer = [=] { + // FIXME(sandboxing): Make this a proper input file. + auto BypassSandbox = sys::sandbox_scoped_disable(); + return MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true); + }(); if (std::error_code EC = Buffer.getError()) { return Diags.Report(diag::err_fe_error_reading) diff --git a/llvm/lib/Support/IOSandboxInternal.h b/llvm/lib/Support/IOSandboxInternal.h index 184b220417fff..72845966d5eb3 100644 --- a/llvm/lib/Support/IOSandboxInternal.h +++ b/llvm/lib/Support/IOSandboxInternal.h @@ -3,6 +3,13 @@ #include "llvm/Support/IOSandbox.h" +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +extern thread_local bool IOSandboxEnabled; + namespace llvm { namespace detail { template struct Interposed; @@ -31,6 +38,9 @@ template constexpr auto interpose(FnTy Fn) { return Interposed{Fn}; } } // namespace detail + +static constexpr auto read = detail::interpose(::read); +static constexpr auto pread = detail::interpose(::pread); } // namespace llvm #endif diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc index 0d991ead72416..fa27f86241b7d 100644 --- a/llvm/lib/Support/Unix/Path.inc +++ b/llvm/lib/Support/Unix/Path.inc @@ -114,6 +114,8 @@ typedef uint_t uint; #define STATVFS_F_FLAG(vfs) (vfs).f_flags #endif +#include "IOSandboxInternal.h" + using namespace llvm; namespace llvm { @@ -197,7 +199,7 @@ std::string getMainExecutable(const char *argv0, void *MainAddr) { uint32_t size = sizeof(exe_path); if (_NSGetExecutablePath(exe_path, &size) == 0) { char link_path[PATH_MAX]; - if (realpath(exe_path, link_path)) + if (::realpath(exe_path, link_path)) return link_path; } #elif defined(__FreeBSD__) @@ -368,7 +370,7 @@ ErrorOr disk_space(const Twine &Path) { std::error_code current_path(SmallVectorImpl &result) { result.clear(); - const char *pwd = ::getenv("PWD"); + const char *pwd = getenv("PWD"); llvm::sys::fs::file_status PWDStatus, DotStatus; if (pwd && llvm::sys::path::is_absolute(pwd) && !llvm::sys::fs::status(pwd, PWDStatus) && @@ -381,7 +383,7 @@ std::error_code current_path(SmallVectorImpl &result) { result.resize_for_overwrite(PATH_MAX); while (true) { - if (::getcwd(result.data(), result.size()) == nullptr) { + if (getcwd(result.data(), result.size()) == nullptr) { // See if there was a real error. if (errno != ENOMEM) { result.clear(); @@ -402,7 +404,7 @@ std::error_code set_current_path(const Twine &path) { SmallString<128> path_storage; StringRef p = path.toNullTerminatedStringRef(path_storage); - if (::chdir(p.begin()) == -1) + if (chdir(p.begin()) == -1) return errnoAsErrorCode(); return std::error_code(); @@ -413,7 +415,7 @@ std::error_code create_directory(const Twine &path, bool IgnoreExisting, SmallString<128> path_storage; StringRef p = path.toNullTerminatedStringRef(path_storage); - if (::mkdir(p.begin(), Perms) == -1) { + if (mkdir(p.begin(), Perms) == -1) { if (errno != EEXIST || !IgnoreExisting) return errnoAsErrorCode(); } @@ -430,7 +432,7 @@ std::error_code create_link(const Twine &to, const Twine &from) { StringRef f = from.toNullTerminatedStringRef(from_storage); StringRef t = to.toNullTerminatedStringRef(to_storage); - if (::symlink(t.begin(), f.begin()) == -1) + if (symlink(t.begin(), f.begin()) == -1) return errnoAsErrorCode(); return std::error_code(); @@ -443,7 +445,7 @@ std::error_code create_hard_link(const Twine &to, const Twine &from) { StringRef f = from.toNullTerminatedStringRef(from_storage); StringRef t = to.toNullTerminatedStringRef(to_storage); - if (::link(t.begin(), f.begin()) == -1) + if (link(t.begin(), f.begin()) == -1) return errnoAsErrorCode(); return std::error_code(); @@ -594,7 +596,7 @@ std::error_code rename(const Twine &from, const Twine &to) { std::error_code resize_file(int FD, uint64_t Size) { // Use ftruncate as a fallback. It may or may not allocate space. At least on // OS X with HFS+ it does. - if (::ftruncate(FD, Size) == -1) + if (ftruncate(FD, Size) == -1) return errnoAsErrorCode(); return std::error_code(); @@ -764,13 +766,13 @@ std::error_code status(const Twine &Path, file_status &Result, bool Follow) { StringRef P = Path.toNullTerminatedStringRef(PathStorage); struct stat Status; - int StatRet = (Follow ? ::stat : ::lstat)(P.begin(), &Status); + int StatRet = (Follow ? stat : lstat)(P.begin(), &Status); return fillStatus(StatRet, Status, Result); } std::error_code status(int FD, file_status &Result) { struct stat Status; - int StatRet = ::fstat(FD, &Status); + int StatRet = fstat(FD, &Status); return fillStatus(StatRet, Status, Result); } @@ -786,13 +788,13 @@ std::error_code setPermissions(const Twine &Path, perms Permissions) { SmallString<128> PathStorage; StringRef P = Path.toNullTerminatedStringRef(PathStorage); - if (::chmod(P.begin(), Permissions)) + if (chmod(P.begin(), Permissions)) return errnoAsErrorCode(); return std::error_code(); } std::error_code setPermissions(int FD, perms Permissions) { - if (::fchmod(FD, Permissions)) + if (fchmod(FD, Permissions)) return errnoAsErrorCode(); return std::error_code(); } @@ -803,7 +805,7 @@ std::error_code setLastAccessAndModificationTime(int FD, TimePoint<> AccessTime, timespec Times[2]; Times[0] = sys::toTimeSpec(AccessTime); Times[1] = sys::toTimeSpec(ModificationTime); - if (::futimens(FD, Times)) + if (futimens(FD, Times)) return errnoAsErrorCode(); return std::error_code(); #elif defined(HAVE_FUTIMES) @@ -813,7 +815,7 @@ std::error_code setLastAccessAndModificationTime(int FD, TimePoint<> AccessTime, Times[1] = sys::toTimeVal(std::chrono::time_point_cast( ModificationTime)); - if (::futimes(FD, Times)) + if (futimes(FD, Times)) return errnoAsErrorCode(); return std::error_code(); #elif defined(__MVS__) @@ -861,7 +863,7 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset, } #endif // #if defined (__APPLE__) - Mapping = ::mmap(nullptr, Size, prot, flags, FD, Offset); + Mapping = mmap(nullptr, Size, prot, flags, FD, Offset); if (Mapping == MAP_FAILED) return errnoAsErrorCode(); return std::error_code(); @@ -878,7 +880,7 @@ mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length, void mapped_file_region::unmapImpl() { if (Mapping) - ::munmap(Mapping, Size); + munmap(Mapping, Size); } std::error_code mapped_file_region::sync() const { @@ -906,7 +908,7 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it, StringRef path, bool follow_symlinks) { SmallString<128> path_null(path); - DIR *directory = ::opendir(path_null.c_str()); + DIR *directory = opendir(path_null.c_str()); if (!directory) return errnoAsErrorCode(); @@ -919,7 +921,7 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it, std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) { if (it.IterationHandle) - ::closedir(reinterpret_cast(it.IterationHandle)); + closedir(reinterpret_cast(it.IterationHandle)); it.IterationHandle = 0; it.CurrentEntry = directory_entry(); return std::error_code(); @@ -941,7 +943,7 @@ static file_type direntType(dirent *Entry) { std::error_code detail::directory_iterator_increment(detail::DirIterState &It) { errno = 0; - dirent *CurDir = ::readdir(reinterpret_cast(It.IterationHandle)); + dirent *CurDir = readdir(reinterpret_cast(It.IterationHandle)); if (CurDir == nullptr && errno != 0) { return errnoAsErrorCode(); } else if (CurDir != nullptr) { @@ -976,7 +978,7 @@ ErrorOr directory_entry::status() const { static bool hasProcSelfFD() { // If we have a /proc filesystem mounted, we can quickly establish the // real name of the file with readlink - static const bool Result = (::access("/proc/self/fd", R_OK) == 0); + static const bool Result = (access("/proc/self/fd", R_OK) == 0); return Result; } #endif @@ -1031,9 +1033,9 @@ std::error_code openFile(const Twine &Name, int &ResultFD, SmallString<128> Storage; StringRef P = Name.toNullTerminatedStringRef(Storage); - // Call ::open in a lambda to avoid overload resolution in RetryAfterSignal + // Call open in a lambda to avoid overload resolution in RetryAfterSignal // when open is overloaded, such as in Bionic. - auto Open = [&]() { return ::open(P.begin(), OpenFlags, Mode); }; + auto Open = [&]() { return open(P.begin(), OpenFlags, Mode); }; if ((ResultFD = sys::RetryAfterSignal(-1, Open)) < 0) return errnoAsErrorCode(); #ifndef O_CLOEXEC @@ -1155,7 +1157,7 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD, // When F_GETPATH is availble, it is the quickest way to get // the real path name. char Buffer[PATH_MAX]; - if (::fcntl(ResultFD, F_GETPATH, Buffer) != -1) + if (fcntl(ResultFD, F_GETPATH, Buffer) != -1) RealPath->append(Buffer, Buffer + strlen(Buffer)); #else char Buffer[PATH_MAX]; @@ -1163,7 +1165,7 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD, if (hasProcSelfFD()) { char ProcPath[64]; snprintf(ProcPath, sizeof(ProcPath), "/proc/self/fd/%d", ResultFD); - ssize_t CharCount = ::readlink(ProcPath, Buffer, sizeof(Buffer)); + ssize_t CharCount = readlink(ProcPath, Buffer, sizeof(Buffer)); if (CharCount > 0) RealPath->append(Buffer, Buffer + CharCount); } else { @@ -1171,8 +1173,8 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD, SmallString<128> Storage; StringRef P = Name.toNullTerminatedStringRef(Storage); - // Use ::realpath to get the real path name - if (::realpath(P.begin(), Buffer) != nullptr) + // Use realpath to get the real path name + if (realpath(P.begin(), Buffer) != nullptr) RealPath->append(Buffer, Buffer + strlen(Buffer)); #if defined(TRY_PROC_SELF_FD) } @@ -1224,11 +1226,11 @@ Expected readNativeFileSlice(file_t FD, MutableArrayRef Buf, #endif #ifdef HAVE_PREAD ssize_t NumRead = - sys::RetryAfterSignal(-1, ::pread, FD, Buf.data(), Size, Offset); + sys::RetryAfterSignal(-1, pread, FD, Buf.data(), Size, Offset); #else if (lseek(FD, Offset, SEEK_SET) == -1) return errorCodeToError(errnoAsErrorCode()); - ssize_t NumRead = sys::RetryAfterSignal(-1, ::read, FD, Buf.data(), Size); + ssize_t NumRead = sys::RetryAfterSignal(-1, read, FD, Buf.data(), Size); #endif if (NumRead == -1) return errorCodeToError(errnoAsErrorCode()); @@ -1253,7 +1255,7 @@ std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout, Lock.l_whence = SEEK_SET; Lock.l_start = 0; Lock.l_len = 0; - if (::fcntl(FD, F_SETLK, &Lock) != -1) + if (fcntl(FD, F_SETLK, &Lock) != -1) return std::error_code(); int Error = errno; if (Error != EACCES && Error != EAGAIN) @@ -1279,7 +1281,7 @@ std::error_code lockFile(int FD, LockKind Kind) { Lock.l_whence = SEEK_SET; Lock.l_start = 0; Lock.l_len = 0; - if (::fcntl(FD, F_SETLKW, &Lock) != -1) + if (fcntl(FD, F_SETLKW, &Lock) != -1) return std::error_code(); return errnoAsErrorCode(); } @@ -1290,7 +1292,7 @@ std::error_code unlockFile(int FD) { Lock.l_whence = SEEK_SET; Lock.l_start = 0; Lock.l_len = 0; - if (::fcntl(FD, F_SETLK, &Lock) != -1) + if (fcntl(FD, F_SETLK, &Lock) != -1) return std::error_code(); return errnoAsErrorCode(); } @@ -1357,14 +1359,14 @@ std::error_code real_path(const Twine &path, SmallVectorImpl &dest, SmallString<128> Storage; StringRef P = path.toNullTerminatedStringRef(Storage); char Buffer[PATH_MAX]; - if (::realpath(P.begin(), Buffer) == nullptr) + if (realpath(P.begin(), Buffer) == nullptr) return errnoAsErrorCode(); dest.append(Buffer, Buffer + strlen(Buffer)); return std::error_code(); } std::error_code changeFileOwnership(int FD, uint32_t Owner, uint32_t Group) { - auto FChown = [&]() { return ::fchown(FD, Owner, Group); }; + auto FChown = [&]() { return fchown(FD, Owner, Group); }; // Retry if fchown call fails due to interruption. if ((sys::RetryAfterSignal(-1, FChown)) < 0) return errnoAsErrorCode(); diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp index 1d27d02a27f47..ecd50a8004f70 100644 --- a/llvm/lib/Support/VirtualFileSystem.cpp +++ b/llvm/lib/Support/VirtualFileSystem.cpp @@ -222,6 +222,7 @@ RealFile::~RealFile() { close(); } ErrorOr RealFile::status() { assert(FD != kInvalidFile && "cannot stat closed file"); if (!S.isStatusKnown()) { + auto BypassSandbox = sys::sandbox_scoped_disable(); file_status RealStatus; if (std::error_code EC = sys::fs::status(FD, RealStatus)) return EC; @@ -238,6 +239,7 @@ ErrorOr> RealFile::getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, bool IsVolatile) { assert(FD != kInvalidFile && "cannot get buffer for closed file"); + auto BypassSandbox = sys::sandbox_scoped_disable(); return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator, IsVolatile); } @@ -307,6 +309,7 @@ class RealFileSystem : public FileSystem { ErrorOr> openFileForReadWithFlags(const Twine &Name, sys::fs::OpenFlags Flags) { + auto BypassSandbox = sys::sandbox_scoped_disable(); SmallString<256> RealName, Storage; Expected FDOrErr = sys::fs::openNativeFileForRead( adjustPath(Name, Storage), Flags, &RealName); @@ -328,6 +331,7 @@ class RealFileSystem : public FileSystem { } // namespace ErrorOr RealFileSystem::status(const Twine &Path) { + auto BypassSandbox = sys::sandbox_scoped_disable(); SmallString<256> Storage; sys::fs::file_status RealStatus; if (std::error_code EC = @@ -352,6 +356,7 @@ llvm::ErrorOr RealFileSystem::getCurrentWorkingDirectory() const { if (WD) return WD->getError(); + auto BypassSandbox = sys::sandbox_scoped_disable(); SmallString<128> Dir; if (std::error_code EC = llvm::sys::fs::current_path(Dir)) return EC; @@ -382,6 +387,7 @@ std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) { std::error_code RealFileSystem::getRealPath(const Twine &Path, SmallVectorImpl &Output) { + auto BypassSandbox = sys::sandbox_scoped_disable(); SmallString<256> Storage; return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output); } @@ -415,12 +421,15 @@ 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) { + RealFSDirIter(const Twine &Path, std::error_code &EC) { + auto BypassSandbox = sys::sandbox_scoped_disable(); + Iter = llvm::sys::fs::directory_iterator{Path, EC}; if (Iter != llvm::sys::fs::directory_iterator()) CurrentEntry = directory_entry(Iter->path(), Iter->type()); } std::error_code increment() override { + auto BypassSandbox = sys::sandbox_scoped_disable(); std::error_code EC; Iter.increment(EC); CurrentEntry = (Iter == llvm::sys::fs::directory_iterator()) From 4a28775603ec70b80d1359509b968dad1bbcfb6f Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Thu, 10 Jul 2025 14:11:14 -0700 Subject: [PATCH 04/20] [llvm][clang] Sandbox IO operations (mmap) --- clang/lib/Driver/Driver.cpp | 3 +++ llvm/lib/Support/IOSandboxInternal.h | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 40ea513e85427..7cf23fdb2b97b 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. + auto BypassSandbox = sandbox_scoped_disable(); // The .crash file can be found on at ~/Library/Logs/DiagnosticReports/ // (or /Library/Logs/DiagnosticReports for root) and has the filename pattern diff --git a/llvm/lib/Support/IOSandboxInternal.h b/llvm/lib/Support/IOSandboxInternal.h index 72845966d5eb3..f784ef7131403 100644 --- a/llvm/lib/Support/IOSandboxInternal.h +++ b/llvm/lib/Support/IOSandboxInternal.h @@ -7,6 +7,9 @@ #ifdef HAVE_UNISTD_H #include #endif +#ifdef HAVE_SYS_MMAN_H +#include +#endif extern thread_local bool IOSandboxEnabled; @@ -41,6 +44,7 @@ template constexpr auto interpose(FnTy Fn) { static constexpr auto read = detail::interpose(::read); static constexpr auto pread = detail::interpose(::pread); +static constexpr auto mmap = detail::interpose(::mmap); } // namespace llvm #endif From 1b017822596f2206c4b343aaff79cc65c4ffa3d3 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Thu, 10 Jul 2025 14:31:20 -0700 Subject: [PATCH 05/20] [llvm][clang] Sandbox IO operations (readdir) --- clang/lib/Serialization/GlobalModuleIndex.cpp | 3 +++ clang/lib/Serialization/ModuleCache.cpp | 4 ++++ llvm/lib/Support/IOSandboxInternal.h | 2 ++ 3 files changed, 9 insertions(+) diff --git a/clang/lib/Serialization/GlobalModuleIndex.cpp b/clang/lib/Serialization/GlobalModuleIndex.cpp index 613121a9596fd..99d90054cb09f 100644 --- a/clang/lib/Serialization/GlobalModuleIndex.cpp +++ b/clang/lib/Serialization/GlobalModuleIndex.cpp @@ -847,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_scoped_disable(); + 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..28772da83b465 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_scoped_disable(); + llvm::SmallString<128> TimestampFile(Path); llvm::sys::path::append(TimestampFile, "modules.timestamp"); diff --git a/llvm/lib/Support/IOSandboxInternal.h b/llvm/lib/Support/IOSandboxInternal.h index f784ef7131403..163c0cfc9dad8 100644 --- a/llvm/lib/Support/IOSandboxInternal.h +++ b/llvm/lib/Support/IOSandboxInternal.h @@ -10,6 +10,7 @@ #ifdef HAVE_SYS_MMAN_H #include #endif +#include extern thread_local bool IOSandboxEnabled; @@ -45,6 +46,7 @@ template constexpr auto interpose(FnTy Fn) { static constexpr auto read = detail::interpose(::read); static constexpr auto pread = detail::interpose(::pread); static constexpr auto mmap = detail::interpose(::mmap); +static constexpr auto readdir = detail::interpose(::readdir); } // namespace llvm #endif From 85f9ca62a382acb393619c53e8032a9395efddb3 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Thu, 10 Jul 2025 15:46:01 -0700 Subject: [PATCH 06/20] [llvm][clang] Sandbox IO operations (stat, lstat, fstat) --- clang/lib/Serialization/ModuleCache.cpp | 2 ++ clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp | 4 ++++ llvm/lib/Support/IOSandboxInternal.h | 4 ++++ llvm/lib/Support/LockFileManager.cpp | 5 +++++ llvm/lib/Support/VirtualOutputBackends.cpp | 5 +++++ llvm/lib/Support/raw_ostream.cpp | 4 ++++ 6 files changed, 24 insertions(+) diff --git a/clang/lib/Serialization/ModuleCache.cpp b/clang/lib/Serialization/ModuleCache.cpp index 28772da83b465..6a3675620977d 100644 --- a/clang/lib/Serialization/ModuleCache.cpp +++ b/clang/lib/Serialization/ModuleCache.cpp @@ -119,6 +119,8 @@ class CrossProcessModuleCache : public ModuleCache { } std::time_t getModuleTimestamp(StringRef ModuleFilename) override { + // This is a compiler-internal input/output, let's bypass the sandbox. + auto SandboxBypass = llvm::sys::sandbox_scoped_disable(); std::string TimestampFilename = serialization::ModuleFile::getTimestampFilename(ModuleFilename); llvm::sys::fs::file_status Status; diff --git a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 217b853305ed1..11e53a5132563 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 @@ -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 SandboxBypass = llvm::sys::sandbox_scoped_disable(); + // Create the HTML directory if it is missing. if (!createdDir) { createdDir = true; diff --git a/llvm/lib/Support/IOSandboxInternal.h b/llvm/lib/Support/IOSandboxInternal.h index 163c0cfc9dad8..044b7dd9bd781 100644 --- a/llvm/lib/Support/IOSandboxInternal.h +++ b/llvm/lib/Support/IOSandboxInternal.h @@ -4,6 +4,7 @@ #include "llvm/Support/IOSandbox.h" #include +#include #ifdef HAVE_UNISTD_H #include #endif @@ -47,6 +48,9 @@ static constexpr auto read = detail::interpose(::read); static constexpr auto pread = detail::interpose(::pread); static constexpr auto mmap = detail::interpose(::mmap); static constexpr auto readdir = detail::interpose(::readdir); +static constexpr auto stat = detail::interpose(::stat); +static constexpr auto lstat = detail::interpose(::lstat); +static constexpr auto fstat = detail::interpose(::fstat); } // namespace llvm #endif diff --git a/llvm/lib/Support/LockFileManager.cpp b/llvm/lib/Support/LockFileManager.cpp index cdded51108b50..54c42e4dd53e6 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" @@ -51,6 +52,8 @@ using namespace llvm; /// \returns The process ID of the process that owns this lock file std::optional LockFileManager::readLockFile(StringRef LockFileName) { + auto SandboxBypass = sys::sandbox_scoped_disable(); + // Read the owning host and PID out of the lock file. If it appears that the // owning process is dead, the lock file is invalid. ErrorOr> MBOrErr = @@ -246,6 +249,8 @@ Expected LockFileManager::tryLock() { } LockFileManager::~LockFileManager() { + auto SandboxBypass = sys::sandbox_scoped_disable(); + if (!std::holds_alternative(Owner)) return; diff --git a/llvm/lib/Support/VirtualOutputBackends.cpp b/llvm/lib/Support/VirtualOutputBackends.cpp index de59b8ab63a53..82ee6df891120 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" @@ -553,6 +554,8 @@ Error OnDiskOutputFile::keep() { } Error OnDiskOutputFile::discard() { + auto BypassSandbox = sys::sandbox_scoped_disable(); + // Destroy the streams to flush them. if (auto E = reset()) return E; @@ -582,6 +585,8 @@ Error OnDiskOutputBackend::makeAbsolute(SmallVectorImpl &Path) const { Expected> OnDiskOutputBackend::createFileImpl(StringRef Path, std::optional Config) { + auto BypassSandbox = sys::sandbox_scoped_disable(); + SmallString<256> AbsPath; if (Path != "-") { AbsPath = Path; diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp index 07b99896543bd..0ea5f024c82c5 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" @@ -617,6 +618,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_scoped_disable(); + if (FD < 0 ) { ShouldClose = false; return; From 637ff5eeac4e4f4c2f5230e6ffffb3a0d14d816c Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Tue, 15 Jul 2025 15:50:29 -0700 Subject: [PATCH 07/20] [llvm][clang] Sandbox IO operations (getcwd) --- llvm/lib/Support/IOSandboxInternal.h | 1 + 1 file changed, 1 insertion(+) diff --git a/llvm/lib/Support/IOSandboxInternal.h b/llvm/lib/Support/IOSandboxInternal.h index 044b7dd9bd781..9e7bb7d6f4567 100644 --- a/llvm/lib/Support/IOSandboxInternal.h +++ b/llvm/lib/Support/IOSandboxInternal.h @@ -51,6 +51,7 @@ static constexpr auto readdir = detail::interpose(::readdir); static constexpr auto stat = detail::interpose(::stat); static constexpr auto lstat = detail::interpose(::lstat); static constexpr auto fstat = detail::interpose(::fstat); +static constexpr auto getcwd = detail::interpose(::getcwd); } // namespace llvm #endif From 54de166adeafd87417663272f42ed6025be44bf9 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Tue, 15 Jul 2025 15:51:46 -0700 Subject: [PATCH 08/20] [llvm][clang] Sandbox IO operations (realpath) --- llvm/lib/Support/IOSandboxInternal.h | 1 + 1 file changed, 1 insertion(+) diff --git a/llvm/lib/Support/IOSandboxInternal.h b/llvm/lib/Support/IOSandboxInternal.h index 9e7bb7d6f4567..80fa79220cc54 100644 --- a/llvm/lib/Support/IOSandboxInternal.h +++ b/llvm/lib/Support/IOSandboxInternal.h @@ -52,6 +52,7 @@ static constexpr auto stat = detail::interpose(::stat); static constexpr auto lstat = detail::interpose(::lstat); static constexpr auto fstat = detail::interpose(::fstat); static constexpr auto getcwd = detail::interpose(::getcwd); +static constexpr auto realpath = detail::interpose(::realpath); } // namespace llvm #endif From 84e59fa0ca27ed7671e248101d398c1687b18041 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Tue, 15 Jul 2025 15:52:09 -0700 Subject: [PATCH 09/20] [llvm][clang] Sandbox IO operations (readlink) --- llvm/lib/Support/IOSandboxInternal.h | 1 + 1 file changed, 1 insertion(+) diff --git a/llvm/lib/Support/IOSandboxInternal.h b/llvm/lib/Support/IOSandboxInternal.h index 80fa79220cc54..b7b8b10a87e78 100644 --- a/llvm/lib/Support/IOSandboxInternal.h +++ b/llvm/lib/Support/IOSandboxInternal.h @@ -53,6 +53,7 @@ static constexpr auto lstat = detail::interpose(::lstat); static constexpr auto fstat = detail::interpose(::fstat); static constexpr auto getcwd = detail::interpose(::getcwd); static constexpr auto realpath = detail::interpose(::realpath); +static constexpr auto readlink = detail::interpose(::readlink); } // namespace llvm #endif From 175f70e761a73e1599083cf4b34e48ed8372ed0c Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Tue, 15 Jul 2025 15:52:51 -0700 Subject: [PATCH 10/20] [llvm][clang] Sandbox IO operations (access) --- llvm/lib/Support/IOSandboxInternal.h | 1 + llvm/lib/Support/Unix/Path.inc | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Support/IOSandboxInternal.h b/llvm/lib/Support/IOSandboxInternal.h index b7b8b10a87e78..3e953c53c6e2a 100644 --- a/llvm/lib/Support/IOSandboxInternal.h +++ b/llvm/lib/Support/IOSandboxInternal.h @@ -48,6 +48,7 @@ static constexpr auto read = detail::interpose(::read); static constexpr auto pread = detail::interpose(::pread); static constexpr auto mmap = detail::interpose(::mmap); static constexpr auto readdir = detail::interpose(::readdir); +static constexpr auto access = detail::interpose(::access); static constexpr auto stat = detail::interpose(::stat); static constexpr auto lstat = detail::interpose(::lstat); static constexpr auto fstat = detail::interpose(::fstat); diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc index fa27f86241b7d..f51c847a5f457 100644 --- a/llvm/lib/Support/Unix/Path.inc +++ b/llvm/lib/Support/Unix/Path.inc @@ -623,7 +623,7 @@ std::error_code access(const Twine &Path, AccessMode Mode) { SmallString<128> PathStorage; StringRef P = Path.toNullTerminatedStringRef(PathStorage); - if (::access(P.begin(), convertAccessMode(Mode)) == -1) + if (llvm::access(P.begin(), convertAccessMode(Mode)) == -1) return errnoAsErrorCode(); if (Mode == AccessMode::Execute) { @@ -978,7 +978,7 @@ ErrorOr directory_entry::status() const { static bool hasProcSelfFD() { // If we have a /proc filesystem mounted, we can quickly establish the // real name of the file with readlink - static const bool Result = (access("/proc/self/fd", R_OK) == 0); + static const bool Result = (llvm::access("/proc/self/fd", R_OK) == 0); return Result; } #endif From 17ca1e05d57a085e05735840207f174f49877287 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Wed, 1 Oct 2025 15:44:03 -0700 Subject: [PATCH 11/20] Rename, re-layer, add build setting --- clang/lib/CodeGen/BackendUtil.cpp | 2 +- clang/lib/Driver/Driver.cpp | 2 +- clang/lib/Driver/Job.cpp | 2 +- clang/lib/Serialization/GlobalModuleIndex.cpp | 4 +- clang/lib/Serialization/ModuleCache.cpp | 4 +- .../StaticAnalyzer/Core/HTMLDiagnostics.cpp | 2 +- clang/tools/driver/cc1_main.cpp | 4 +- clang/tools/driver/cc1as_main.cpp | 4 +- llvm/CMakeLists.txt | 1 + llvm/include/llvm/Support/IOSandbox.h | 72 +++++++++++++++++-- llvm/lib/Support/IOSandbox.cpp | 19 ----- llvm/lib/Support/IOSandboxInternal.h | 60 ---------------- llvm/lib/Support/LockFileManager.cpp | 4 +- llvm/lib/Support/Signals.cpp | 2 +- llvm/lib/Support/Unix/Path.inc | 26 ++++++- llvm/lib/Support/VirtualFileSystem.cpp | 20 +++--- llvm/lib/Support/VirtualOutputBackends.cpp | 4 +- llvm/lib/Support/raw_ostream.cpp | 2 +- 18 files changed, 121 insertions(+), 113 deletions(-) delete mode 100644 llvm/lib/Support/IOSandbox.cpp delete mode 100644 llvm/lib/Support/IOSandboxInternal.h diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 3066bd8313777..5a53e2a106b8b 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -1436,7 +1436,7 @@ void clang::emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts, std::unique_ptr EmptyModule; if (!CGOpts.ThinLTOIndexFile.empty()) { // FIXME(sandboxing): Figure out how to support distributed indexing. - auto BypassSandbox = sys::sandbox_scoped_disable(); + 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. diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 7cf23fdb2b97b..95a09cd3afe3f 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -1872,7 +1872,7 @@ bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename, 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_scoped_disable(); + 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 diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index ab6cd2973dd6e..da7a1f2e07e90 100644 --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -429,7 +429,7 @@ int CC1Command::Execute(ArrayRef> Redirects, // Enabling the sandbox here allows us to restore its previous state even when // this cc1 invocation crashes. - auto EnableSandbox = llvm::sys::sandbox_scoped_enable(); + auto EnableSandbox = llvm::sys::sandbox::scopedEnable(); llvm::CrashRecoveryContext CRC; CRC.DumpStackAndCleanupOnFailure = true; diff --git a/clang/lib/Serialization/GlobalModuleIndex.cpp b/clang/lib/Serialization/GlobalModuleIndex.cpp index 99d90054cb09f..2246a3ac0a57e 100644 --- a/clang/lib/Serialization/GlobalModuleIndex.cpp +++ b/clang/lib/Serialization/GlobalModuleIndex.cpp @@ -252,7 +252,7 @@ GlobalModuleIndex::~GlobalModuleIndex() { std::pair GlobalModuleIndex::readIndex(StringRef Path) { // This is a compiler-internal input/output, let's bypass the sandbox. - auto BypassSandbox = llvm::sys::sandbox_scoped_disable(); + auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); // Load the index file, if it's there. llvm::SmallString<128> IndexPath; @@ -848,7 +848,7 @@ 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_scoped_disable(); + auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); llvm::SmallString<128> IndexPath; IndexPath += Path; diff --git a/clang/lib/Serialization/ModuleCache.cpp b/clang/lib/Serialization/ModuleCache.cpp index 6a3675620977d..1a521920e9dd6 100644 --- a/clang/lib/Serialization/ModuleCache.cpp +++ b/clang/lib/Serialization/ModuleCache.cpp @@ -29,7 +29,7 @@ void clang::maybePruneImpl(StringRef Path, time_t PruneInterval, return; // This is a compiler-internal input/output, let's bypass the sandbox. - auto BypassSandbox = llvm::sys::sandbox_scoped_disable(); + auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); llvm::SmallString<128> TimestampFile(Path); llvm::sys::path::append(TimestampFile, "modules.timestamp"); @@ -120,7 +120,7 @@ class CrossProcessModuleCache : public ModuleCache { std::time_t getModuleTimestamp(StringRef ModuleFilename) override { // This is a compiler-internal input/output, let's bypass the sandbox. - auto SandboxBypass = llvm::sys::sandbox_scoped_disable(); + auto SandboxBypass = llvm::sys::sandbox::scopedDisable(); std::string TimestampFilename = serialization::ModuleFile::getTimestampFilename(ModuleFilename); llvm::sys::fs::file_status Status; diff --git a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 11e53a5132563..aae4f4161fad4 100644 --- a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -259,7 +259,7 @@ void HTMLDiagnostics::FlushDiagnosticsImpl( void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, FilesMade *filesMade) { // FIXME(sandboxing): Remove this by adopting `llvm::vfs::OutputBackend`. - auto SandboxBypass = llvm::sys::sandbox_scoped_disable(); + auto SandboxBypass = llvm::sys::sandbox::scopedDisable(); // Create the HTML directory if it is missing. if (!createdDir) { diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp index af30ba68dfde4..579b441e5cb9c 100644 --- a/clang/tools/driver/cc1_main.cpp +++ b/clang/tools/driver/cc1_main.cpp @@ -274,7 +274,7 @@ int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { /// Create the actual file system. auto VFS = [] { - auto BypassSandbox = llvm::sys::sandbox_scoped_disable(); + auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); return llvm::vfs::getRealFileSystem(); }(); Clang->createVirtualFileSystem(std::move(VFS), DiagsBuffer); @@ -309,7 +309,7 @@ int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { // results now. This happens in -disable-free mode. { // This isn't a formal input or output of the compiler. - auto BypassSandbox = llvm::sys::sandbox_scoped_disable(); + auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); std::unique_ptr IOFile = llvm::CreateInfoOutputFile(); if (Clang->getCodeGenOpts().TimePassesJson) { *IOFile << "{\n"; diff --git a/clang/tools/driver/cc1as_main.cpp b/clang/tools/driver/cc1as_main.cpp index 285a128add1e1..c2f45ee370fb3 100644 --- a/clang/tools/driver/cc1as_main.cpp +++ b/clang/tools/driver/cc1as_main.cpp @@ -428,7 +428,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts, ErrorOr> Buffer = [=] { // FIXME(sandboxing): Make this a proper input file. - auto BypassSandbox = sys::sandbox_scoped_disable(); + auto BypassSandbox = sys::sandbox::scopedDisable(); return MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true); }(); @@ -677,7 +677,7 @@ int cc1as_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { DiagnosticsEngine Diags(DiagnosticIDs::create(), DiagOpts, DiagClient); auto VFS = [] { - auto BypassSandbox = sys::sandbox_scoped_disable(); + auto BypassSandbox = sys::sandbox::scopedDisable(); return vfs::getRealFileSystem(); }(); diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt index c450ee5a3d72e..ac81506a02993 100644 --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -697,6 +697,7 @@ option(LLVM_ENABLE_WERROR "Fail and stop if a warning is triggered." OFF) option(LLVM_ENABLE_DUMP "Enable dump functions even when assertions are disabled" OFF) option(LLVM_UNREACHABLE_OPTIMIZE "Optimize llvm_unreachable() as undefined behavior (default), guaranteed trap when OFF" ON) +option(LLVM_ENABLE_IO_SANDBOX "Enable IO sandboxing in supported tools" OFF) if( NOT uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG" ) option(LLVM_ENABLE_ASSERTIONS "Enable assertions" OFF) diff --git a/llvm/include/llvm/Support/IOSandbox.h b/llvm/include/llvm/Support/IOSandbox.h index 1a8fb9788e3f6..cf6f4f2b857a0 100644 --- a/llvm/include/llvm/Support/IOSandbox.h +++ b/llvm/include/llvm/Support/IOSandbox.h @@ -9,12 +9,74 @@ #ifndef LLVM_SUPPORT_IOSANDBOX_H #define LLVM_SUPPORT_IOSANDBOX_H +// Always enable IO sandboxing in debug/assert builds for development, +// but allow enablement even for release/no-assert builds for production. +#if !defined(NDEBUG) || defined(LLVM_ENABLE_IO_SANDBOX) + +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SaveAndRestore.h" -namespace llvm::sys { -SaveAndRestore sandbox_scoped_enable(); -SaveAndRestore sandbox_scoped_disable(); -void sandbox_violation_if_enabled(); -} // namespace llvm::sys +namespace llvm::sys::sandbox { +inline thread_local bool Enabled = false; +inline SaveAndRestore scopedEnable() { return {Enabled, true}; } +inline SaveAndRestore scopedDisable() { return {Enabled, false}; } +inline void violationIfEnabled() { + if (Enabled) + reportFatalInternalError("IO sandbox violation"); +} +} // namespace llvm::sys::sandbox + +#else + +namespace llvm::sys::sandbox { +inline int scopedEnable() {} +inline int scopedDisable() {} +inline void violationIfEnabled() {} +} // namespace llvm::sys::sandbox + +#endif + +namespace llvm::sys::sandbox { +/// Facility for seamlessly interposing function calls and sandbox enforcement. +/// This is intended for creating static functors like so: +/// +/// // before +/// #include +/// namespace x { +/// void perform_read() { read(); } // not sandboxed +/// } +/// +/// // after +/// #include +/// namespace x { +/// static constexpr auto read = llvm::sys::sandbox::interpose(::read); +/// void perform_read() { read(); } // sandboxed +/// } +template struct Interposed; + +template struct Interposed { + RetTy (*Fn)(ArgTy...); + + RetTy operator()(ArgTy... Arg) const { + violationIfEnabled(); + return Fn(std::forward(Arg)...); + } +}; + +template +struct Interposed { + RetTy (*Fn)(ArgTy..., ...); + + template + RetTy operator()(ArgTy... Arg, CVarArgTy... CVarArg) const { + violationIfEnabled(); + return Fn(std::forward(Arg)..., std::forward(CVarArg)...); + } +}; + +template constexpr auto interpose(FnTy Fn) { + return Interposed{Fn}; +} +} // namespace llvm::sys::sandbox #endif diff --git a/llvm/lib/Support/IOSandbox.cpp b/llvm/lib/Support/IOSandbox.cpp deleted file mode 100644 index a046d38b48fc3..0000000000000 --- a/llvm/lib/Support/IOSandbox.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "llvm/Support/IOSandbox.h" - -#include - -using namespace llvm; - -thread_local bool IOSandboxEnabled = false; - -SaveAndRestore sys::sandbox_scoped_enable() { - return {IOSandboxEnabled, true}; -} - -SaveAndRestore sys::sandbox_scoped_disable() { - return {IOSandboxEnabled, false}; -} - -void sys::sandbox_violation_if_enabled() { - assert(!IOSandboxEnabled && "sandbox violation"); -} diff --git a/llvm/lib/Support/IOSandboxInternal.h b/llvm/lib/Support/IOSandboxInternal.h deleted file mode 100644 index 3e953c53c6e2a..0000000000000 --- a/llvm/lib/Support/IOSandboxInternal.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef LLVM_SUPPORT_IOSANDBOXINTERNAL_H -#define LLVM_SUPPORT_IOSANDBOXINTERNAL_H - -#include "llvm/Support/IOSandbox.h" - -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_SYS_MMAN_H -#include -#endif -#include - -extern thread_local bool IOSandboxEnabled; - -namespace llvm { -namespace detail { -template struct Interposed; - -template struct Interposed { - RetTy (*Fn)(ArgTy...); - - RetTy operator()(ArgTy... Arg) const { - sys::sandbox_violation_if_enabled(); - return Fn(std::forward(Arg)...); - } -}; - -template -struct Interposed { - RetTy (*Fn)(ArgTy..., ...); - - template - RetTy operator()(ArgTy... Arg, CVarArgTy... CVarArg) const { - sys::sandbox_violation_if_enabled(); - return Fn(std::forward(Arg)..., std::forward(CVarArg)...); - } -}; - -template constexpr auto interpose(FnTy Fn) { - return Interposed{Fn}; -} -} // namespace detail - -static constexpr auto read = detail::interpose(::read); -static constexpr auto pread = detail::interpose(::pread); -static constexpr auto mmap = detail::interpose(::mmap); -static constexpr auto readdir = detail::interpose(::readdir); -static constexpr auto access = detail::interpose(::access); -static constexpr auto stat = detail::interpose(::stat); -static constexpr auto lstat = detail::interpose(::lstat); -static constexpr auto fstat = detail::interpose(::fstat); -static constexpr auto getcwd = detail::interpose(::getcwd); -static constexpr auto realpath = detail::interpose(::realpath); -static constexpr auto readlink = detail::interpose(::readlink); -} // namespace llvm - -#endif diff --git a/llvm/lib/Support/LockFileManager.cpp b/llvm/lib/Support/LockFileManager.cpp index 54c42e4dd53e6..795bffbbc648c 100644 --- a/llvm/lib/Support/LockFileManager.cpp +++ b/llvm/lib/Support/LockFileManager.cpp @@ -52,7 +52,7 @@ using namespace llvm; /// \returns The process ID of the process that owns this lock file std::optional LockFileManager::readLockFile(StringRef LockFileName) { - auto SandboxBypass = sys::sandbox_scoped_disable(); + auto SandboxBypass = sys::sandbox::scopedDisable(); // Read the owning host and PID out of the lock file. If it appears that the // owning process is dead, the lock file is invalid. @@ -249,7 +249,7 @@ Expected LockFileManager::tryLock() { } LockFileManager::~LockFileManager() { - auto SandboxBypass = sys::sandbox_scoped_disable(); + auto SandboxBypass = sys::sandbox::scopedDisable(); if (!std::holds_alternative(Owner)) return; diff --git a/llvm/lib/Support/Signals.cpp b/llvm/lib/Support/Signals.cpp index 076ec293750af..f160a135f623d 100644 --- a/llvm/lib/Support/Signals.cpp +++ b/llvm/lib/Support/Signals.cpp @@ -97,7 +97,7 @@ CallBacksToRun() { // Signal-safe. void sys::RunSignalHandlers() { // Let's not interfere with stack trace symbolication and friends. - auto BypassSandbox = sandbox_scoped_disable(); + auto BypassSandbox = sandbox::scopedDisable(); for (CallbackAndCookie &RunMe : CallBacksToRun()) { auto Expected = CallbackAndCookie::Status::Initialized; diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc index f51c847a5f457..ba99c07f3b463 100644 --- a/llvm/lib/Support/Unix/Path.inc +++ b/llvm/lib/Support/Unix/Path.inc @@ -114,7 +114,31 @@ typedef uint_t uint; #define STATVFS_F_FLAG(vfs) (vfs).f_flags #endif -#include "IOSandboxInternal.h" +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_MMAN_H +#include +#endif +#include + +#include "llvm/Support/IOSandbox.h" + +namespace llvm { +static constexpr auto read = sys::sandbox::interpose(::read); +static constexpr auto pread = sys::sandbox::interpose(::pread); +static constexpr auto mmap = sys::sandbox::interpose(::mmap); +static constexpr auto readdir = sys::sandbox::interpose(::readdir); +static constexpr auto access = sys::sandbox::interpose(::access); +static constexpr auto stat = sys::sandbox::interpose(::stat); +static constexpr auto lstat = sys::sandbox::interpose(::lstat); +static constexpr auto fstat = sys::sandbox::interpose(::fstat); +static constexpr auto getcwd = sys::sandbox::interpose(::getcwd); +static constexpr auto realpath = sys::sandbox::interpose(::realpath); +static constexpr auto readlink = sys::sandbox::interpose(::readlink); +} // namespace llvm using namespace llvm; diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp index ecd50a8004f70..62fe07d3e4af0 100644 --- a/llvm/lib/Support/VirtualFileSystem.cpp +++ b/llvm/lib/Support/VirtualFileSystem.cpp @@ -222,7 +222,7 @@ RealFile::~RealFile() { close(); } ErrorOr RealFile::status() { assert(FD != kInvalidFile && "cannot stat closed file"); if (!S.isStatusKnown()) { - auto BypassSandbox = sys::sandbox_scoped_disable(); + auto BypassSandbox = sys::sandbox::scopedDisable(); file_status RealStatus; if (std::error_code EC = sys::fs::status(FD, RealStatus)) return EC; @@ -239,7 +239,7 @@ ErrorOr> RealFile::getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, bool IsVolatile) { assert(FD != kInvalidFile && "cannot get buffer for closed file"); - auto BypassSandbox = sys::sandbox_scoped_disable(); + auto BypassSandbox = sys::sandbox::scopedDisable(); return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator, IsVolatile); } @@ -309,7 +309,7 @@ class RealFileSystem : public FileSystem { ErrorOr> openFileForReadWithFlags(const Twine &Name, sys::fs::OpenFlags Flags) { - auto BypassSandbox = sys::sandbox_scoped_disable(); + auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<256> RealName, Storage; Expected FDOrErr = sys::fs::openNativeFileForRead( adjustPath(Name, Storage), Flags, &RealName); @@ -331,7 +331,7 @@ class RealFileSystem : public FileSystem { } // namespace ErrorOr RealFileSystem::status(const Twine &Path) { - auto BypassSandbox = sys::sandbox_scoped_disable(); + auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<256> Storage; sys::fs::file_status RealStatus; if (std::error_code EC = @@ -356,7 +356,7 @@ llvm::ErrorOr RealFileSystem::getCurrentWorkingDirectory() const { if (WD) return WD->getError(); - auto BypassSandbox = sys::sandbox_scoped_disable(); + auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<128> Dir; if (std::error_code EC = llvm::sys::fs::current_path(Dir)) return EC; @@ -387,7 +387,7 @@ std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) { std::error_code RealFileSystem::getRealPath(const Twine &Path, SmallVectorImpl &Output) { - auto BypassSandbox = sys::sandbox_scoped_disable(); + auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<256> Storage; return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output); } @@ -406,12 +406,12 @@ void RealFileSystem::printImpl(raw_ostream &OS, PrintType Type, IntrusiveRefCntPtr vfs::getRealFileSystem() { static IntrusiveRefCntPtr FS = makeIntrusiveRefCnt(true); - sys::sandbox_violation_if_enabled(); + sys::sandbox::violationIfEnabled(); return FS; } std::unique_ptr vfs::createPhysicalFileSystem() { - sys::sandbox_violation_if_enabled(); + sys::sandbox::violationIfEnabled(); return std::make_unique(false); } @@ -422,14 +422,14 @@ class RealFSDirIter : public llvm::vfs::detail::DirIterImpl { public: RealFSDirIter(const Twine &Path, std::error_code &EC) { - auto BypassSandbox = sys::sandbox_scoped_disable(); + auto BypassSandbox = sys::sandbox::scopedDisable(); Iter = llvm::sys::fs::directory_iterator{Path, EC}; if (Iter != llvm::sys::fs::directory_iterator()) CurrentEntry = directory_entry(Iter->path(), Iter->type()); } std::error_code increment() override { - auto BypassSandbox = sys::sandbox_scoped_disable(); + auto BypassSandbox = sys::sandbox::scopedDisable(); std::error_code EC; Iter.increment(EC); CurrentEntry = (Iter == llvm::sys::fs::directory_iterator()) diff --git a/llvm/lib/Support/VirtualOutputBackends.cpp b/llvm/lib/Support/VirtualOutputBackends.cpp index 82ee6df891120..61b29b1955bca 100644 --- a/llvm/lib/Support/VirtualOutputBackends.cpp +++ b/llvm/lib/Support/VirtualOutputBackends.cpp @@ -554,7 +554,7 @@ Error OnDiskOutputFile::keep() { } Error OnDiskOutputFile::discard() { - auto BypassSandbox = sys::sandbox_scoped_disable(); + auto BypassSandbox = sys::sandbox::scopedDisable(); // Destroy the streams to flush them. if (auto E = reset()) @@ -585,7 +585,7 @@ Error OnDiskOutputBackend::makeAbsolute(SmallVectorImpl &Path) const { Expected> OnDiskOutputBackend::createFileImpl(StringRef Path, std::optional Config) { - auto BypassSandbox = sys::sandbox_scoped_disable(); + auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<256> AbsPath; if (Path != "-") { diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp index 0ea5f024c82c5..3b608706f6c55 100644 --- a/llvm/lib/Support/raw_ostream.cpp +++ b/llvm/lib/Support/raw_ostream.cpp @@ -619,7 +619,7 @@ 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_scoped_disable(); + auto BypassSandbox = sys::sandbox::scopedDisable(); if (FD < 0 ) { ShouldClose = false; From 5ac2edd54f8b85635b52cede3404ed4021630846 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Mon, 6 Oct 2025 14:32:55 -0700 Subject: [PATCH 12/20] [[maybe_unused]], proper CMake flag setup --- clang/lib/CodeGen/BackendUtil.cpp | 2 +- clang/lib/Driver/Driver.cpp | 2 +- clang/lib/Driver/Job.cpp | 2 +- clang/lib/Serialization/GlobalModuleIndex.cpp | 4 ++-- clang/lib/Serialization/ModuleCache.cpp | 4 ++-- .../lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp | 2 +- clang/tools/driver/cc1_main.cpp | 4 ++-- clang/tools/driver/cc1as_main.cpp | 4 ++-- llvm/include/llvm/Config/llvm-config.h.cmake | 3 +++ llvm/include/llvm/Support/IOSandbox.h | 4 ++-- llvm/lib/Support/LockFileManager.cpp | 4 ++-- llvm/lib/Support/Signals.cpp | 2 +- llvm/lib/Support/VirtualFileSystem.cpp | 16 ++++++++-------- llvm/lib/Support/VirtualOutputBackends.cpp | 4 ++-- llvm/lib/Support/raw_ostream.cpp | 2 +- 15 files changed, 31 insertions(+), 28 deletions(-) diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 5a53e2a106b8b..4a57dedb0e800 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -1436,7 +1436,7 @@ void clang::emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts, std::unique_ptr EmptyModule; if (!CGOpts.ThinLTOIndexFile.empty()) { // FIXME(sandboxing): Figure out how to support distributed indexing. - auto BypassSandbox = sys::sandbox::scopedDisable(); + [[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. diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 95a09cd3afe3f..ede5fb0e94423 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -1872,7 +1872,7 @@ bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename, 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(); + [[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 diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index da7a1f2e07e90..e52d1069b4209 100644 --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -429,7 +429,7 @@ int CC1Command::Execute(ArrayRef> Redirects, // Enabling the sandbox here allows us to restore its previous state even when // this cc1 invocation crashes. - auto EnableSandbox = llvm::sys::sandbox::scopedEnable(); + [[maybe_unused]] auto EnableSandbox = llvm::sys::sandbox::scopedEnable(); llvm::CrashRecoveryContext CRC; CRC.DumpStackAndCleanupOnFailure = true; diff --git a/clang/lib/Serialization/GlobalModuleIndex.cpp b/clang/lib/Serialization/GlobalModuleIndex.cpp index 2246a3ac0a57e..297f5645b1645 100644 --- a/clang/lib/Serialization/GlobalModuleIndex.cpp +++ b/clang/lib/Serialization/GlobalModuleIndex.cpp @@ -252,7 +252,7 @@ GlobalModuleIndex::~GlobalModuleIndex() { std::pair GlobalModuleIndex::readIndex(StringRef Path) { // This is a compiler-internal input/output, let's bypass the sandbox. - auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); // Load the index file, if it's there. llvm::SmallString<128> IndexPath; @@ -848,7 +848,7 @@ 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(); + [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); llvm::SmallString<128> IndexPath; IndexPath += Path; diff --git a/clang/lib/Serialization/ModuleCache.cpp b/clang/lib/Serialization/ModuleCache.cpp index 1a521920e9dd6..1ae4d6fdc6247 100644 --- a/clang/lib/Serialization/ModuleCache.cpp +++ b/clang/lib/Serialization/ModuleCache.cpp @@ -29,7 +29,7 @@ void clang::maybePruneImpl(StringRef Path, time_t PruneInterval, return; // This is a compiler-internal input/output, let's bypass the sandbox. - auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); llvm::SmallString<128> TimestampFile(Path); llvm::sys::path::append(TimestampFile, "modules.timestamp"); @@ -120,7 +120,7 @@ class CrossProcessModuleCache : public ModuleCache { std::time_t getModuleTimestamp(StringRef ModuleFilename) override { // This is a compiler-internal input/output, let's bypass the sandbox. - auto SandboxBypass = llvm::sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); std::string TimestampFilename = serialization::ModuleFile::getTimestampFilename(ModuleFilename); llvm::sys::fs::file_status Status; diff --git a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index aae4f4161fad4..b201b644359e9 100644 --- a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -259,7 +259,7 @@ void HTMLDiagnostics::FlushDiagnosticsImpl( void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, FilesMade *filesMade) { // FIXME(sandboxing): Remove this by adopting `llvm::vfs::OutputBackend`. - auto SandboxBypass = llvm::sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); // Create the HTML directory if it is missing. if (!createdDir) { diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp index 579b441e5cb9c..7da2f95eb5fdc 100644 --- a/clang/tools/driver/cc1_main.cpp +++ b/clang/tools/driver/cc1_main.cpp @@ -274,7 +274,7 @@ int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { /// Create the actual file system. auto VFS = [] { - auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); return llvm::vfs::getRealFileSystem(); }(); Clang->createVirtualFileSystem(std::move(VFS), DiagsBuffer); @@ -309,7 +309,7 @@ int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { // results now. This happens in -disable-free mode. { // This isn't a formal input or output of the compiler. - auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); std::unique_ptr IOFile = llvm::CreateInfoOutputFile(); if (Clang->getCodeGenOpts().TimePassesJson) { *IOFile << "{\n"; diff --git a/clang/tools/driver/cc1as_main.cpp b/clang/tools/driver/cc1as_main.cpp index c2f45ee370fb3..eed2bb59f32bf 100644 --- a/clang/tools/driver/cc1as_main.cpp +++ b/clang/tools/driver/cc1as_main.cpp @@ -428,7 +428,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts, ErrorOr> Buffer = [=] { // FIXME(sandboxing): Make this a proper input file. - auto BypassSandbox = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); return MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true); }(); @@ -677,7 +677,7 @@ int cc1as_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { DiagnosticsEngine Diags(DiagnosticIDs::create(), DiagOpts, DiagClient); auto VFS = [] { - auto BypassSandbox = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); return vfs::getRealFileSystem(); }(); 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 index cf6f4f2b857a0..db0e134c48e5f 100644 --- a/llvm/include/llvm/Support/IOSandbox.h +++ b/llvm/include/llvm/Support/IOSandbox.h @@ -29,8 +29,8 @@ inline void violationIfEnabled() { #else namespace llvm::sys::sandbox { -inline int scopedEnable() {} -inline int scopedDisable() {} +inline int scopedEnable() { return 0; } +inline int scopedDisable() { return 0; } inline void violationIfEnabled() {} } // namespace llvm::sys::sandbox diff --git a/llvm/lib/Support/LockFileManager.cpp b/llvm/lib/Support/LockFileManager.cpp index 795bffbbc648c..586f4c98ae32c 100644 --- a/llvm/lib/Support/LockFileManager.cpp +++ b/llvm/lib/Support/LockFileManager.cpp @@ -52,7 +52,7 @@ using namespace llvm; /// \returns The process ID of the process that owns this lock file std::optional LockFileManager::readLockFile(StringRef LockFileName) { - auto SandboxBypass = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); // Read the owning host and PID out of the lock file. If it appears that the // owning process is dead, the lock file is invalid. @@ -249,7 +249,7 @@ Expected LockFileManager::tryLock() { } LockFileManager::~LockFileManager() { - auto SandboxBypass = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); if (!std::holds_alternative(Owner)) return; diff --git a/llvm/lib/Support/Signals.cpp b/llvm/lib/Support/Signals.cpp index f160a135f623d..786c4576f13e3 100644 --- a/llvm/lib/Support/Signals.cpp +++ b/llvm/lib/Support/Signals.cpp @@ -97,7 +97,7 @@ CallBacksToRun() { // Signal-safe. void sys::RunSignalHandlers() { // Let's not interfere with stack trace symbolication and friends. - auto BypassSandbox = sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sandbox::scopedDisable(); for (CallbackAndCookie &RunMe : CallBacksToRun()) { auto Expected = CallbackAndCookie::Status::Initialized; diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp index 62fe07d3e4af0..0683cf8b3bd1f 100644 --- a/llvm/lib/Support/VirtualFileSystem.cpp +++ b/llvm/lib/Support/VirtualFileSystem.cpp @@ -222,7 +222,7 @@ RealFile::~RealFile() { close(); } ErrorOr RealFile::status() { assert(FD != kInvalidFile && "cannot stat closed file"); if (!S.isStatusKnown()) { - auto BypassSandbox = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); file_status RealStatus; if (std::error_code EC = sys::fs::status(FD, RealStatus)) return EC; @@ -239,7 +239,7 @@ ErrorOr> RealFile::getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, bool IsVolatile) { assert(FD != kInvalidFile && "cannot get buffer for closed file"); - auto BypassSandbox = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator, IsVolatile); } @@ -309,7 +309,7 @@ class RealFileSystem : public FileSystem { ErrorOr> openFileForReadWithFlags(const Twine &Name, sys::fs::OpenFlags Flags) { - auto BypassSandbox = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<256> RealName, Storage; Expected FDOrErr = sys::fs::openNativeFileForRead( adjustPath(Name, Storage), Flags, &RealName); @@ -331,7 +331,7 @@ class RealFileSystem : public FileSystem { } // namespace ErrorOr RealFileSystem::status(const Twine &Path) { - auto BypassSandbox = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<256> Storage; sys::fs::file_status RealStatus; if (std::error_code EC = @@ -356,7 +356,7 @@ llvm::ErrorOr RealFileSystem::getCurrentWorkingDirectory() const { if (WD) return WD->getError(); - auto BypassSandbox = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<128> Dir; if (std::error_code EC = llvm::sys::fs::current_path(Dir)) return EC; @@ -387,7 +387,7 @@ std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) { std::error_code RealFileSystem::getRealPath(const Twine &Path, SmallVectorImpl &Output) { - auto BypassSandbox = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<256> Storage; return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output); } @@ -422,14 +422,14 @@ class RealFSDirIter : public llvm::vfs::detail::DirIterImpl { public: RealFSDirIter(const Twine &Path, std::error_code &EC) { - auto BypassSandbox = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); Iter = llvm::sys::fs::directory_iterator{Path, EC}; if (Iter != llvm::sys::fs::directory_iterator()) CurrentEntry = directory_entry(Iter->path(), Iter->type()); } std::error_code increment() override { - auto BypassSandbox = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); std::error_code EC; Iter.increment(EC); CurrentEntry = (Iter == llvm::sys::fs::directory_iterator()) diff --git a/llvm/lib/Support/VirtualOutputBackends.cpp b/llvm/lib/Support/VirtualOutputBackends.cpp index 61b29b1955bca..90b4c3173b28d 100644 --- a/llvm/lib/Support/VirtualOutputBackends.cpp +++ b/llvm/lib/Support/VirtualOutputBackends.cpp @@ -554,7 +554,7 @@ Error OnDiskOutputFile::keep() { } Error OnDiskOutputFile::discard() { - auto BypassSandbox = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); // Destroy the streams to flush them. if (auto E = reset()) @@ -585,7 +585,7 @@ Error OnDiskOutputBackend::makeAbsolute(SmallVectorImpl &Path) const { Expected> OnDiskOutputBackend::createFileImpl(StringRef Path, std::optional Config) { - auto BypassSandbox = sys::sandbox::scopedDisable(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<256> AbsPath; if (Path != "-") { diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp index 3b608706f6c55..99ac3e1d75b47 100644 --- a/llvm/lib/Support/raw_ostream.cpp +++ b/llvm/lib/Support/raw_ostream.cpp @@ -619,7 +619,7 @@ 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(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); if (FD < 0 ) { ShouldClose = false; From 0792e518f2f53a44e1d0c5b22f439756c5031f9f Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Mon, 6 Oct 2025 14:48:46 -0700 Subject: [PATCH 13/20] noexcept --- llvm/include/llvm/Support/IOSandbox.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/llvm/include/llvm/Support/IOSandbox.h b/llvm/include/llvm/Support/IOSandbox.h index db0e134c48e5f..400767f711d58 100644 --- a/llvm/include/llvm/Support/IOSandbox.h +++ b/llvm/include/llvm/Support/IOSandbox.h @@ -54,21 +54,22 @@ namespace llvm::sys::sandbox { /// } template struct Interposed; -template struct Interposed { +template +struct Interposed { RetTy (*Fn)(ArgTy...); - RetTy operator()(ArgTy... Arg) const { + RetTy operator()(ArgTy... Arg) const noexcept(NE) { violationIfEnabled(); return Fn(std::forward(Arg)...); } }; -template -struct Interposed { +template +struct Interposed { RetTy (*Fn)(ArgTy..., ...); template - RetTy operator()(ArgTy... Arg, CVarArgTy... CVarArg) const { + RetTy operator()(ArgTy... Arg, CVarArgTy... CVarArg) const noexcept(NE) { violationIfEnabled(); return Fn(std::forward(Arg)..., std::forward(CVarArg)...); } From f5fb3fddd31b8bbf9b72b9d4314cf6e68218e4b4 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Mon, 6 Oct 2025 15:27:55 -0700 Subject: [PATCH 14/20] MSVC noexcept(X) workaround --- llvm/include/llvm/Support/IOSandbox.h | 32 ++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/llvm/include/llvm/Support/IOSandbox.h b/llvm/include/llvm/Support/IOSandbox.h index 400767f711d58..e5267fe2b5c39 100644 --- a/llvm/include/llvm/Support/IOSandbox.h +++ b/llvm/include/llvm/Support/IOSandbox.h @@ -54,22 +54,42 @@ namespace llvm::sys::sandbox { /// } template struct Interposed; -template -struct Interposed { +template struct Interposed { RetTy (*Fn)(ArgTy...); - RetTy operator()(ArgTy... Arg) const noexcept(NE) { + RetTy operator()(ArgTy... Arg) const { violationIfEnabled(); return Fn(std::forward(Arg)...); } }; -template -struct Interposed { +template +struct Interposed { + RetTy (*Fn)(ArgTy...) noexcept; + + RetTy operator()(ArgTy... Arg) const noexcept { + violationIfEnabled(); + return Fn(std::forward(Arg)...); + } +}; + +template +struct Interposed { RetTy (*Fn)(ArgTy..., ...); template - RetTy operator()(ArgTy... Arg, CVarArgTy... CVarArg) const noexcept(NE) { + RetTy operator()(ArgTy... Arg, CVarArgTy... CVarArg) const { + violationIfEnabled(); + return Fn(std::forward(Arg)..., std::forward(CVarArg)...); + } +}; + +template +struct Interposed { + RetTy (*Fn)(ArgTy..., ...) noexcept; + + template + RetTy operator()(ArgTy... Arg, CVarArgTy... CVarArg) const noexcept { violationIfEnabled(); return Fn(std::forward(Arg)..., std::forward(CVarArg)...); } From 5d6acd9e20e1205cc93dc2a5d6f015e00c6e0387 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Tue, 7 Oct 2025 12:16:47 -0700 Subject: [PATCH 15/20] Initial version of a clang-tidy check --- .../clang-tidy/llvm/CMakeLists.txt | 1 + .../clang-tidy/llvm/IOSandboxCheck.cpp | 319 ++++++++++++++++++ .../clang-tidy/llvm/IOSandboxCheck.h | 31 ++ .../clang-tidy/llvm/LLVMTidyModule.cpp | 2 + 4 files changed, 353 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/llvm/IOSandboxCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/llvm/IOSandboxCheck.h diff --git a/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt index 78ef0444305ff..73c8dc4f17696 100644 --- a/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt @@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangTidyLLVMModule STATIC HeaderGuardCheck.cpp IncludeOrderCheck.cpp + IOSandboxCheck.cpp LLVMTidyModule.cpp PreferIsaOrDynCastInConditionalsCheck.cpp PreferRegisterOverUnsignedCheck.cpp diff --git a/clang-tools-extra/clang-tidy/llvm/IOSandboxCheck.cpp b/clang-tools-extra/clang-tidy/llvm/IOSandboxCheck.cpp new file mode 100644 index 0000000000000..92247524ee548 --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvm/IOSandboxCheck.cpp @@ -0,0 +1,319 @@ +//===--- FilesystemAccessCheck.cpp - clang-tidy --------------------------===// +// +// Enforces controlled filesystem access patterns +// +//===----------------------------------------------------------------------===// + +#include "IOSandboxCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::llvm_check { +// Low-level filesystem functions that should only be called from llvm::sys::fs +static const llvm::StringSet<> &getForbiddenFilesystemFunctions() { + static const llvm::StringSet<> Functions = { + // POSIX file operations + "open", + "openat", + "creat", + "close", + "read", + "write", + "pread", + "pwrite", + "lseek", + "ftruncate", + "truncate", + "stat", + "fstat", + "lstat", + "fstatat", + "access", + "faccessat", + "chmod", + "fchmod", + "fchmodat", + "chown", + "fchown", + "lchown", + "fchownat", + "link", + "linkat", + "symlink", + "symlinkat", + "readlink", + "readlinkat", + "unlink", + "unlinkat", + "remove", + "rename", + "renameat", + "mkdir", + "mkdirat", + "rmdir", + "opendir", + "readdir", + "closedir", + "fdopendir", + "chdir", + "fchdir", + "getcwd", + "dup", + "dup2", + "dup3", + "fcntl", + "pipe", + "pipe2", + "mkfifo", + "mkfifoat", + "mknod", + "mknodat", + "utimes", + "futimes", + "utimensat", + "futimens", + + // C standard library file operations + "fopen", + "freopen", + "fclose", + "fflush", + "fread", + "fwrite", + "fgetc", + "fputc", + "fgets", + "fputs", + "fseek", + "ftell", + "rewind", + "fgetpos", + "fsetpos", + "tmpfile", + "tmpnam", + "tempnam", + + // Windows file operations + "CreateFileA", + "CreateFileW", + "CreateFile", + "ReadFile", + "WriteFile", + "CloseHandle", + "DeleteFileA", + "DeleteFileW", + "DeleteFile", + "MoveFileA", + "MoveFileW", + "MoveFile", + "CopyFileA", + "CopyFileW", + "CopyFile", + "GetFileAttributesA", + "GetFileAttributesW", + "GetFileAttributes", + "SetFileAttributesA", + "SetFileAttributesW", + "SetFileAttributes", + "CreateDirectoryA", + "CreateDirectoryW", + "CreateDirectory", + "RemoveDirectoryA", + "RemoveDirectoryW", + "RemoveDirectory", + "FindFirstFileA", + "FindFirstFileW", + "FindFirstFile", + "FindNextFileA", + "FindNextFileW", + "FindNextFile", + "FindClose", + "GetCurrentDirectoryA", + "GetCurrentDirectoryW", + "SetCurrentDirectoryA", + "SetCurrentDirectoryW", + + // Memory-mapped files + "mmap", + "munmap", + "mprotect", + "msync", + "MapViewOfFile", + "UnmapViewOfFile", + }; + return Functions; +} + +static bool isInLLVMSysFsNamespace(const FunctionDecl *FD) { + if (!FD) + return false; + + auto IsAnonymousNamespace = [](const DeclContext *DC) { + if (!DC) + return false; + const auto *ND = dyn_cast(DC); + if (!ND) + return false; + return ND->isAnonymousNamespace(); + }; + + auto GetNamedNamespace = [](const DeclContext *DC) -> const NamespaceDecl * { + if (!DC) + return nullptr; + const auto *ND = dyn_cast(DC); + if (!ND) + return nullptr; + if (ND->isAnonymousNamespace()) + return nullptr; + return ND; + }; + + const DeclContext *DC = FD->getDeclContext(); + + // Walk up the context chain looking for llvm::sys::fs + SmallVector ReverseNamespaces; + while (IsAnonymousNamespace(DC)) + DC = DC->getParent(); + while (const auto *ND = GetNamedNamespace(DC)) { + ReverseNamespaces.push_back(ND->getName()); + DC = DC->getParent(); + } + auto Namespaces = llvm::reverse(ReverseNamespaces); + + return llvm::equal(Namespaces, SmallVector{"llvm", "sys", "fs"}); +} + +static bool isLLVMSysFsCall(const CallExpr *CE) { + if (!CE) + return false; + + const FunctionDecl *Callee = CE->getDirectCallee(); + if (!Callee) + return false; + + return isInLLVMSysFsNamespace(Callee) && !Callee->isOverloadedOperator(); +} + +static bool isForbiddenFilesystemCall(const CallExpr *CE) { + if (!CE) + return false; + + const FunctionDecl *Callee = CE->getDirectCallee(); + if (!Callee) + return false; + + const auto &ForbiddenFuncs = getForbiddenFilesystemFunctions(); + + return ForbiddenFuncs.contains(Callee->getQualifiedNameAsString()); +} + +static bool hasSandboxBypass(const FunctionDecl *FD, SourceLocation CallLoc) { + if (!FD || !FD->hasBody()) + return false; + + const Stmt *Body = FD->getBody(); + if (!Body) + return false; + + // Look for variable declarations of the bypass type + // We need to check if the bypass variable is declared before the call site + class BypassFinder : public RecursiveASTVisitor { + public: + bool FoundBypass = false; + SourceLocation CallLocation; + const SourceManager *SM; + + bool VisitVarDecl(VarDecl *VD) { + if (!VD) + return true; + + // Check if this is a sandbox bypass variable + const Type *T = VD->getType().getTypePtrOrNull(); + if (!T) + return true; + + const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); + if (!RD) + return true; + + // Check for ScopedSandboxDisable or similar RAII types + std::string TypeName = RD->getQualifiedNameAsString(); + if (TypeName.find("ScopedSandboxDisable") != std::string::npos || + TypeName.find("scopedDisable") != std::string::npos) { + + // Check if this declaration comes before the call + if (SM && + SM->isBeforeInTranslationUnit(VD->getLocation(), CallLocation)) { + FoundBypass = true; + return false; // Stop searching + } + } + + return true; + } + }; + + BypassFinder Finder; + Finder.CallLocation = CallLoc; + Finder.SM = &FD->getASTContext().getSourceManager(); + Finder.TraverseStmt(const_cast(Body)); + + return Finder.FoundBypass; +} + +void IOSandboxCheck::registerMatchers(MatchFinder *Finder) { + // Match any call expression within a function. + Finder->addMatcher( + callExpr(hasAncestor(functionDecl().bind("parent_func"))).bind("call"), + this); + + // Also match variable declarations to find sandbox bypass objects + Finder->addMatcher( + varDecl(hasType(cxxRecordDecl(hasName("ScopedSandboxDisable"))), + hasAncestor(functionDecl().bind("func_with_bypass"))) + .bind("bypass_var"), + this); +} + +void IOSandboxCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Call = Result.Nodes.getNodeAs("call"); + const auto *ParentFunc = Result.Nodes.getNodeAs("parent_func"); + + if (!Call || !ParentFunc) + return; + + // Skip system headers and template instantiations + if (Call->getBeginLoc().isInvalid() || + Result.Context->getSourceManager().isInSystemHeader( + Call->getBeginLoc()) || + ParentFunc->isTemplateInstantiation()) + return; + + // Rule 1: Check if calling llvm::sys::fs without sandbox bypass + if (isLLVMSysFsCall(Call)) { + if (!hasSandboxBypass(ParentFunc, Call->getBeginLoc())) { + diag(Call->getBeginLoc(), "call to llvm::sys::fs function") + << Call->getSourceRange(); + } + return; // Don't check rule 2 for llvm::sys::fs calls + } + + // Rule 2: Check if calling forbidden filesystem functions outside + // llvm::sys::fs + if (isForbiddenFilesystemCall(Call)) { + if (!isInLLVMSysFsNamespace(ParentFunc)) { + const auto *Callee = Call->getDirectCallee(); + std::string CalleeName = Callee ? Callee->getNameAsString() : "unknown"; + + diag(Call->getBeginLoc(), + "low-level filesystem function '%0' may only be called from a " + "llvm::sys::fs function") + << CalleeName << Call->getSourceRange(); + } + } +} +} // namespace clang::tidy::llvm_check diff --git a/clang-tools-extra/clang-tidy/llvm/IOSandboxCheck.h b/clang-tools-extra/clang-tidy/llvm/IOSandboxCheck.h new file mode 100644 index 0000000000000..99886989bdae7 --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvm/IOSandboxCheck.h @@ -0,0 +1,31 @@ +//===--- LLVMFilesystemAccessCheck.h - clang-tidy ---------------*- C++ -*-===// +// +// Enforces controlled filesystem access patterns +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_IOSANDBOXCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_IOSANDBOXCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::llvm_check { + +/// Enforces two rules for filesystem access: +/// 1. Functions calling llvm::sys::fs must have a sandbox bypass RAII object +/// 2. Only llvm::sys::fs functions may call low-level filesystem functions +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/llvm/io-sandbox.html +class IOSandboxCheck : public ClangTidyCheck { +public: + IOSandboxCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace clang::tidy::llvm_check + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_IOSANDBOXCHECK_H diff --git a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp index ed65cd1720457..be32495746035 100644 --- a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp @@ -13,6 +13,7 @@ #include "../readability/NamespaceCommentCheck.h" #include "../readability/QualifiedAutoCheck.h" #include "HeaderGuardCheck.h" +#include "IOSandboxCheck.h" #include "IncludeOrderCheck.h" #include "PreferIsaOrDynCastInConditionalsCheck.h" #include "PreferRegisterOverUnsignedCheck.h" @@ -31,6 +32,7 @@ class LLVMModule : public ClangTidyModule { "llvm-else-after-return"); CheckFactories.registerCheck("llvm-header-guard"); CheckFactories.registerCheck("llvm-include-order"); + CheckFactories.registerCheck("llvm-io-sandbox"); CheckFactories.registerCheck( "llvm-namespace-comment"); CheckFactories.registerCheck( From 26abceea738edd6abea4e061463711c0aa6b40dd Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Wed, 15 Oct 2025 16:22:44 -0700 Subject: [PATCH 16/20] AsmPrinter + BTFDebug --- llvm/include/llvm/CodeGen/AsmPrinter.h | 3 --- llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 1 - llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp | 9 +++++++-- llvm/lib/Target/BPF/BTFDebug.cpp | 10 ++++++++-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h index 9ace2555b4b62..0c73eddca3526 100644 --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -110,9 +110,6 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass { /// generating (such as the current section etc). std::unique_ptr OutStreamer; - /// The VFS to resolve asm include directives. - IntrusiveRefCntPtr 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 e2af0c5925248..06262f9ce3147 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -477,7 +477,6 @@ void AsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const { } bool AsmPrinter::doInitialization(Module &M) { - VFS = vfs::getRealFileSystem(); auto *MMIWP = getAnalysisIfAvailable(); 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..6b0fd456391e2 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp @@ -98,8 +98,13 @@ 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); + // FIXME(sandboxing): This is not executed in tests, but might be common. + // Propagating vfs::FileSystem here is lots of work, + // consider bypassing the sandbox. + if (!MCOptions.IASSearchPaths.empty()) { + SrcMgr.setIncludeDirs(MCOptions.IASSearchPaths); + SrcMgr.setVirtualFileSystem(vfs::getRealFileSystem()); + } std::unique_ptr Parser( createMCAsmParser(SrcMgr, OutContext, *OutStreamer, *MAI, BufNum)); diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp index ba4b48990c647..f0c82427ad057 100644 --- a/llvm/lib/Target/BPF/BTFDebug.cpp +++ b/llvm/lib/Target/BPF/BTFDebug.cpp @@ -23,6 +23,7 @@ #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCStreamer.h" +#include "llvm/Support/IOSandbox.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Target/TargetLoweringObjectFile.h" @@ -1017,12 +1018,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 Buf; auto Source = File->getSource(); if (Source) Buf = MemoryBuffer::getMemBufferCopy(*Source); - else if (ErrorOr> BufOrErr = - MemoryBuffer::getFile(FileName)) + else if (ErrorOr> BufOrErr = LoadFile(FileName)) Buf = std::move(*BufOrErr); if (Buf) for (line_iterator I(*Buf, false), E; I != E; ++I) From 64820bde80cce25ea26ba868d81a4a68c1890b63 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Wed, 15 Oct 2025 19:45:05 -0700 Subject: [PATCH 17/20] Proper sandbox --- llvm/include/llvm/CodeGen/AsmPrinter.h | 5 ----- .../lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp | 14 +++++++------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h index 0c73eddca3526..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: diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp index 6b0fd456391e2..8c2467037b4cb 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" @@ -98,13 +99,12 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI, unsigned BufNum = addInlineAsmDiagBuffer(Str, LocMDNode); SourceMgr &SrcMgr = *MMI->getContext().getInlineSourceManager(); - // FIXME(sandboxing): This is not executed in tests, but might be common. - // Propagating vfs::FileSystem here is lots of work, - // consider bypassing the sandbox. - if (!MCOptions.IASSearchPaths.empty()) { - SrcMgr.setIncludeDirs(MCOptions.IASSearchPaths); - SrcMgr.setVirtualFileSystem(vfs::getRealFileSystem()); - } + SrcMgr.setIncludeDirs(MCOptions.IASSearchPaths); + 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 Parser( createMCAsmParser(SrcMgr, OutContext, *OutStreamer, *MAI, BufNum)); From 36094dcae296318a11d69f4e8f5877c24f539449 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Thu, 16 Oct 2025 09:11:58 -0700 Subject: [PATCH 18/20] Sanitizers --- clang/lib/CodeGen/BackendUtil.cpp | 7 ++++--- .../llvm/Transforms/Instrumentation/SanitizerCoverage.h | 7 +++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 4a57dedb0e800..9985a0141f65f 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -716,9 +716,10 @@ static void addSanitizers(const Triple &TargetTriple, ThinOrFullLTOPhase) { if (CodeGenOpts.hasSanitizeCoverage()) { auto SancovOpts = getSancovOptsFromCGOpts(CodeGenOpts); - MPM.addPass(SanitizerCoveragePass( - SancovOpts, CodeGenOpts.SanitizeCoverageAllowlistFiles, - CodeGenOpts.SanitizeCoverageIgnorelistFiles)); + MPM.addPass( + SanitizerCoveragePass(SancovOpts, PB.getVirtualFileSystemPtr(), + CodeGenOpts.SanitizeCoverageAllowlistFiles, + CodeGenOpts.SanitizeCoverageIgnorelistFiles)); } if (CodeGenOpts.hasSanitizeBinaryMetadata()) { diff --git a/llvm/include/llvm/Transforms/Instrumentation/SanitizerCoverage.h b/llvm/include/llvm/Transforms/Instrumentation/SanitizerCoverage.h index f14f5b90a5cc9..f2676d1234222 100644 --- a/llvm/include/llvm/Transforms/Instrumentation/SanitizerCoverage.h +++ b/llvm/include/llvm/Transforms/Instrumentation/SanitizerCoverage.h @@ -32,17 +32,16 @@ class SanitizerCoveragePass : public PassInfoMixin { public: explicit SanitizerCoveragePass( SanitizerCoverageOptions Options = SanitizerCoverageOptions(), + IntrusiveRefCntPtr VFS = vfs::getRealFileSystem(), const std::vector &AllowlistFiles = std::vector(), const std::vector &BlocklistFiles = std::vector()) : Options(Options) { if (AllowlistFiles.size() > 0) - Allowlist = SpecialCaseList::createOrDie(AllowlistFiles, - *vfs::getRealFileSystem()); + Allowlist = SpecialCaseList::createOrDie(AllowlistFiles, *VFS); if (BlocklistFiles.size() > 0) - Blocklist = SpecialCaseList::createOrDie(BlocklistFiles, - *vfs::getRealFileSystem()); + Blocklist = SpecialCaseList::createOrDie(BlocklistFiles, *VFS); } LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); static bool isRequired() { return true; } From 4702b7e42108dbbfb1eee095607ea5fc27e973f5 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Thu, 16 Oct 2025 10:55:29 -0700 Subject: [PATCH 19/20] LockFileManager --- llvm/lib/Support/LockFileManager.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/llvm/lib/Support/LockFileManager.cpp b/llvm/lib/Support/LockFileManager.cpp index 586f4c98ae32c..215f2f1d2699d 100644 --- a/llvm/lib/Support/LockFileManager.cpp +++ b/llvm/lib/Support/LockFileManager.cpp @@ -167,6 +167,8 @@ Expected LockFileManager::tryLock() { assert(std::holds_alternative(Owner) && "lock has already been attempted"); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); + SmallString<128> AbsoluteFileName(FileName); if (std::error_code EC = sys::fs::make_absolute(AbsoluteFileName)) return createStringError(EC, "failed to obtain absolute path for " + @@ -264,6 +266,8 @@ LockFileManager::~LockFileManager() { WaitForUnlockResult LockFileManager::waitForUnlockFor(std::chrono::seconds MaxSeconds) { + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); + auto *LockFileOwner = std::get_if(&Owner); assert(LockFileOwner && "waiting for lock to be unlocked without knowing the owner"); @@ -293,5 +297,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 a354b41956a90bf53b894cdaa7daa3dbea25fab1 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Thu, 16 Oct 2025 14:10:41 -0700 Subject: [PATCH 20/20] [WIP] Windows (alt approach) --- llvm/include/llvm/Support/FileSystem.h | 7 +++++ llvm/lib/Support/Windows/Path.inc | 38 ++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/Support/FileSystem.h b/llvm/include/llvm/Support/FileSystem.h index cf2a8104ac813..15afa42978167 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 #include @@ -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(); 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(); 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; } diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc index be007b7abdb51..32098fbcfa381 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 &result) { + sandbox::violationIfEnabled(); + SmallVector cur_path; DWORD len = MAX_PATH; @@ -226,6 +228,8 @@ std::error_code current_path(SmallVectorImpl &result) { } std::error_code set_current_path(const Twine &path) { + sandbox::violationIfEnabled(); + // Convert to utf-16. SmallVector wide_path; if (std::error_code ec = widenPath(path, wide_path)) @@ -350,6 +354,8 @@ static std::error_code is_local_internal(SmallVectorImpl &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 FinalPath; HANDLE Handle = reinterpret_cast(_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 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 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(_get_osfhandle(FD)); return getStatus(FileHandle, Result); } std::error_code status(file_t FileHandle, file_status &Result) { + sandbox::violationIfEnabled(); + return getStatus(FileHandle, Result); } @@ -1242,6 +1260,8 @@ static std::error_code openNativeFileInternal(const Twine &Name, Expected 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 +1297,8 @@ Expected 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 Result = openNativeFile(Name, Disp, Access, Flags); if (!Result) return errorToErrorCode(Result.takeError()); @@ -1300,12 +1322,16 @@ static std::error_code directoryRealPath(const Twine &Name, std::error_code openFileForRead(const Twine &Name, int &ResultFD, OpenFlags Flags, SmallVectorImpl *RealPath) { + sandbox::violationIfEnabled(); + Expected NativeFile = openNativeFileForRead(Name, Flags, RealPath); return nativeFileToFd(std::move(NativeFile), ResultFD, OF_None); } Expected openNativeFileForRead(const Twine &Name, OpenFlags Flags, SmallVectorImpl *RealPath) { + sandbox::violationIfEnabled(); + Expected Result = openNativeFile(Name, CD_OpenExisting, FA_Read, Flags); @@ -1324,9 +1350,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 readNativeFileImpl(file_t FileHandle, - MutableArrayRef Buf, - OVERLAPPED *Overlap) { +static Expected readNativeFileImpl(file_t FileHandle, + MutableArrayRef 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 +1368,16 @@ Expected readNativeFileImpl(file_t FileHandle, } Expected readNativeFile(file_t FileHandle, MutableArrayRef Buf) { + sandbox::violationIfEnabled(); + return readNativeFileImpl(FileHandle, Buf, /*Overlap=*/nullptr); } Expected readNativeFileSlice(file_t FileHandle, MutableArrayRef Buf, uint64_t Offset) { + sandbox::violationIfEnabled(); + OVERLAPPED Overlapped = {}; Overlapped.Offset = uint32_t(Offset); Overlapped.OffsetHigh = uint32_t(Offset >> 32); @@ -1492,6 +1522,8 @@ void expand_tilde(const Twine &path, SmallVectorImpl &dest) { std::error_code real_path(const Twine &path, SmallVectorImpl &dest, bool expand_tilde) { + sandbox::violationIfEnabled(); + dest.clear(); if (path.isTriviallyEmpty()) return std::error_code();