Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6df2094
[Support] Introduce IO sandboxing
jansvoboda11 Oct 27, 2025
4e9a245
[clang] Enable IO sandboxing in -cc1 and -cc1as
jansvoboda11 Oct 27, 2025
8563871
[Support] Disallow `MemoryBuffer::getFile*()` in IO sandbox
jansvoboda11 Oct 27, 2025
57a8820
[Support] Disallow `vfs::{create,get}RealFileSystem()` in IO sandbox
jansvoboda11 Oct 27, 2025
9d0ed0d
[Support] Disallow input `sys::fs` APIs in IO sandbox
jansvoboda11 Oct 27, 2025
11352c6
[Support] Bless `vfs::RealFileSystem` and friends in IO sandbox
jansvoboda11 Oct 27, 2025
2041bf9
[Support] Allow `LockFileManager` in IO sandbox
jansvoboda11 Oct 27, 2025
11cc742
[Support] Allow `raw_fd_ostream` in IO sandbox
jansvoboda11 Oct 27, 2025
cfb3672
[Support] Allow `OnDiskOutputBackend` in IO sandbox
jansvoboda11 Oct 27, 2025
d888326
[clang] Allow `ModuleCache` and `GlobalModuleIndex` in IO sandbox
jansvoboda11 Oct 27, 2025
d392a1a
[clang] Allow `HTMLDiagnostics` in IO sandbox
jansvoboda11 Oct 27, 2025
7e90667
[clang] Allow ThinLTO indexing in IO sandbox
jansvoboda11 Oct 27, 2025
f9641d9
[clang] Allow crash diagnostic file in IO sandbox
jansvoboda11 Oct 27, 2025
a2386e2
[CodeGen] Allow `AsmPrinter` in IO sandbox
jansvoboda11 Oct 28, 2025
8fa730b
[Target] Allow `BTFDebug` in IO sandbox
jansvoboda11 Oct 28, 2025
fe0fd06
[clang] Allow `FileManager` to read stdin
jansvoboda11 Oct 28, 2025
1bed185
[REMOVE] Force-enable IO sandboxing
jansvoboda11 Oct 31, 2025
79cedef
Make use of `LLVM_THREAD_LOCAL`
jansvoboda11 Nov 8, 2025
a173be8
Drop bypass from `CrossProcessModuleCache::getLock()`
jansvoboda11 Nov 8, 2025
cfc89ae
Sink `directory_iterator` violations from header into the implementat…
jansvoboda11 Nov 8, 2025
8465a0c
Document sandbox on `getRealFileSystem()` and `createPhysicalFileSyst…
jansvoboda11 Nov 8, 2025
7c3ba74
Default IO sandboxing to OFF
jansvoboda11 Dec 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions clang/lib/Basic/FileManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "llvm/ADT/Statistic.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/IOSandbox.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
Expand Down Expand Up @@ -347,12 +348,15 @@ llvm::Expected<FileEntryRef> FileManager::getSTDIN() {
if (STDIN)
return *STDIN;

std::unique_ptr<llvm::MemoryBuffer> Content;
if (auto ContentOrError = llvm::MemoryBuffer::getSTDIN())
Content = std::move(*ContentOrError);
else
auto ContentOrError = [] {
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
return llvm::MemoryBuffer::getSTDIN();
}();

if (!ContentOrError)
return llvm::errorCodeToError(ContentOrError.getError());

auto Content = std::move(*ContentOrError);
STDIN = getVirtualFileRef(Content->getBufferIdentifier(),
Content->getBufferSize(), 0);
FileEntry &FE = const_cast<FileEntry &>(STDIN->getFileEntry());
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/BackendUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,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"
Expand Down Expand Up @@ -1464,6 +1465,8 @@ void clang::emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts,

std::unique_ptr<llvm::Module> EmptyModule;
if (!CGOpts.ThinLTOIndexFile.empty()) {
// FIXME(sandboxing): Figure out how to support distributed indexing.
auto BypassSandbox = sys::sandbox::scopedDisable();
// If we are performing a ThinLTO importing compile, load the function index
// into memory and pass it into runThinLTOBackend, which will run the
// function importer and invoke LTO passes.
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,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"
Expand Down Expand Up @@ -1848,6 +1849,8 @@ bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename,
using namespace llvm::sys;
assert(llvm::Triple(llvm::sys::getProcessTriple()).isOSDarwin() &&
"Only knows about .crash files on Darwin");
// This is not a formal output of the compiler, let's bypass the sandbox.
auto BypassSandbox = sandbox::scopedDisable();

// The .crash file can be found on at ~/Library/Logs/DiagnosticReports/
// (or /Library/Logs/DiagnosticReports for root) and has the filename pattern
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Driver/Job.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -426,6 +427,10 @@ int CC1Command::Execute(ArrayRef<std::optional<StringRef>> Redirects,
if (ExecutionFailed)
*ExecutionFailed = false;

// Enabling the sandbox here allows us to restore its previous state even when
// this cc1 invocation crashes.
auto EnableSandbox = llvm::sys::sandbox::scopedEnable();

llvm::CrashRecoveryContext CRC;
CRC.DumpStackAndCleanupOnFailure = true;

Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Serialization/GlobalModuleIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -250,6 +251,9 @@ GlobalModuleIndex::~GlobalModuleIndex() {

std::pair<GlobalModuleIndex *, llvm::Error>
GlobalModuleIndex::readIndex(StringRef Path) {
// This is a compiler-internal input/output, let's bypass the sandbox.
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();

// Load the index file, if it's there.
llvm::SmallString<128> IndexPath;
IndexPath += Path;
Expand Down Expand Up @@ -843,6 +847,9 @@ llvm::Error
GlobalModuleIndex::writeIndex(FileManager &FileMgr,
const PCHContainerReader &PCHContainerRdr,
StringRef Path) {
// This is a compiler-internal input/output, let's bypass the sandbox.
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();

llvm::SmallString<128> IndexPath;
IndexPath += Path;
llvm::sys::path::append(IndexPath, IndexFileName);
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Serialization/ModuleCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -27,6 +28,9 @@ void clang::maybePruneImpl(StringRef Path, time_t PruneInterval,
if (PruneInterval <= 0 || PruneAfter <= 0)
return;

// This is a compiler-internal input/output, let's bypass the sandbox.
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();

llvm::SmallString<128> TimestampFile(Path);
llvm::sys::path::append(TimestampFile, "modules.timestamp");

Expand Down Expand Up @@ -103,6 +107,9 @@ class CrossProcessModuleCache : public ModuleCache {

public:
void prepareForGetLock(StringRef ModuleFilename) override {
// This is a compiler-internal input/output, let's bypass the sandbox.
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();

// FIXME: Do this in LockFileManager and only if the directory doesn't
// exist.
StringRef Dir = llvm::sys::path::parent_path(ModuleFilename);
Expand All @@ -115,6 +122,9 @@ class CrossProcessModuleCache : public ModuleCache {
}

std::time_t getModuleTimestamp(StringRef ModuleFilename) override {
// This is a compiler-internal input/output, let's bypass the sandbox.
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();

std::string TimestampFilename =
serialization::ModuleFile::getTimestampFilename(ModuleFilename);
llvm::sys::fs::file_status Status;
Expand All @@ -124,6 +134,9 @@ class CrossProcessModuleCache : public ModuleCache {
}

void updateModuleTimestamp(StringRef ModuleFilename) override {
// This is a compiler-internal input/output, let's bypass the sandbox.
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();

// Overwrite the timestamp file contents so that file's mtime changes.
std::error_code EC;
llvm::raw_fd_ostream OS(
Expand All @@ -138,6 +151,9 @@ class CrossProcessModuleCache : public ModuleCache {

void maybePrune(StringRef Path, time_t PruneInterval,
time_t PruneAfter) override {
// This is a compiler-internal input/output, let's bypass the sandbox.
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();

maybePruneImpl(Path, PruneInterval, PruneAfter);
}

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/IOSandbox.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
Expand Down Expand Up @@ -257,6 +258,9 @@ void HTMLDiagnostics::FlushDiagnosticsImpl(

void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
FilesMade *filesMade) {
// FIXME(sandboxing): Remove this by adopting `llvm::vfs::OutputBackend`.
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();

// Create the HTML directory if it is missing.
if (!createdDir) {
createdDir = true;
Expand Down
27 changes: 18 additions & 9 deletions clang/tools/driver/cc1_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,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"
Expand Down Expand Up @@ -273,7 +274,11 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
GetResourcesPath(Argv0, MainAddr);

/// Create the actual file system.
Clang->createVirtualFileSystem(llvm::vfs::getRealFileSystem(), DiagsBuffer);
auto VFS = [] {
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
return llvm::vfs::getRealFileSystem();
}();
Clang->createVirtualFileSystem(std::move(VFS), DiagsBuffer);

// Create the actual diagnostics engine.
Clang->createDiagnostics();
Expand Down Expand Up @@ -303,15 +308,19 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {

// If any timers were active but haven't been destroyed yet, print their
// results now. This happens in -disable-free mode.
std::unique_ptr<raw_ostream> IOFile = llvm::CreateInfoOutputFile();
if (Clang->getCodeGenOpts().TimePassesJson) {
*IOFile << "{\n";
llvm::TimerGroup::printAllJSONValues(*IOFile, "");
*IOFile << "\n}\n";
} else if (!Clang->getCodeGenOpts().TimePassesStatsFile) {
llvm::TimerGroup::printAll(*IOFile);
{
// This isn't a formal input or output of the compiler.
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
std::unique_ptr<raw_ostream> IOFile = llvm::CreateInfoOutputFile();
if (Clang->getCodeGenOpts().TimePassesJson) {
*IOFile << "{\n";
llvm::TimerGroup::printAllJSONValues(*IOFile, "");
*IOFile << "\n}\n";
} else if (!Clang->getCodeGenOpts().TimePassesStatsFile) {
llvm::TimerGroup::printAll(*IOFile);
}
llvm::TimerGroup::clearAll();
}
llvm::TimerGroup::clearAll();

if (llvm::timeTraceProfilerEnabled()) {
if (auto profilerOutput = Clang->createOutputFile(
Expand Down
13 changes: 10 additions & 3 deletions clang/tools/driver/cc1as_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -424,8 +425,11 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
if (!TheTarget)
return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple.str();

ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = [&] {
// FIXME(sandboxing): Make this a proper input file.
auto BypassSandbox = sys::sandbox::scopedDisable();
return MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
}();

if (std::error_code EC = Buffer.getError()) {
return Diags.Report(diag::err_fe_error_reading)
Expand Down Expand Up @@ -671,7 +675,10 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
DiagClient->setPrefix("clang -cc1as");
DiagnosticsEngine Diags(DiagnosticIDs::create(), DiagOpts, DiagClient);

auto VFS = vfs::getRealFileSystem();
auto VFS = [] {
auto BypassSandbox = sys::sandbox::scopedDisable();
return vfs::getRealFileSystem();
}();

// Set an error handler, so that any LLVM backend diagnostics go through our
// error handler.
Expand Down
1 change: 1 addition & 0 deletions llvm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@ else()
option(LLVM_ENABLE_ASSERTIONS "Enable assertions" ON)
endif()

option(LLVM_ENABLE_IO_SANDBOX "Enable IO sandboxing in supported tools" OFF)
option(LLVM_ENABLE_EXPENSIVE_CHECKS "Enable expensive checks" OFF)

set(LLVM_ABI_BREAKING_CHECKS "WITH_ASSERTS" CACHE STRING
Expand Down
8 changes: 0 additions & 8 deletions llvm/include/llvm/CodeGen/AsmPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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/SetVector.h"
#include "llvm/ADT/SmallVector.h"
Expand Down Expand Up @@ -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:
Expand All @@ -110,9 +105,6 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
/// generating (such as the current section etc).
std::unique_ptr<MCStreamer> OutStreamer;

/// The VFS to resolve asm include directives.
IntrusiveRefCntPtr<vfs::FileSystem> VFS;

/// The current machine function.
MachineFunction *MF = nullptr;

Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/Config/llvm-config.h.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
42 changes: 42 additions & 0 deletions llvm/include/llvm/Support/IOSandbox.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===- IOSandbox.h ----------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_SUPPORT_IOSANDBOX_H
#define LLVM_SUPPORT_IOSANDBOX_H

#if defined(LLVM_ENABLE_IO_SANDBOX) && LLVM_ENABLE_IO_SANDBOX

#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SaveAndRestore.h"

namespace llvm::sys::sandbox {
inline LLVM_THREAD_LOCAL bool Enabled = false;
struct ScopedSetting {
SaveAndRestore<bool> Impl;
};
inline ScopedSetting scopedEnable() { return {{Enabled, true}}; }
inline ScopedSetting scopedDisable() { return {{Enabled, false}}; }
inline void violationIfEnabled() {
if (Enabled)
reportFatalInternalError("IO sandbox violation");
}
} // namespace llvm::sys::sandbox

#else

namespace llvm::sys::sandbox {
struct [[maybe_unused]] ScopedSetting {};
inline ScopedSetting scopedEnable() { return {}; }
inline ScopedSetting scopedDisable() { return {}; }
inline void violationIfEnabled() {}
} // namespace llvm::sys::sandbox

#endif

#endif
2 changes: 2 additions & 0 deletions llvm/include/llvm/Support/VirtualFileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -372,12 +372,14 @@ class LLVM_ABI FileSystem : public llvm::ThreadSafeRefCountedBase<FileSystem>,
/// the operating system.
/// The working directory is linked to the process's working directory.
/// (This is usually thread-hostile).
/// This may only be called outside the IO sandbox.
LLVM_ABI IntrusiveRefCntPtr<FileSystem> getRealFileSystem();

/// Create an \p vfs::FileSystem for the 'real' file system, as seen by
/// the operating system.
/// It has its own working directory, independent of (but initially equal to)
/// that of the process.
/// This may only be called outside the IO sandbox.
LLVM_ABI std::unique_ptr<FileSystem> createPhysicalFileSystem();

/// A file system that allows overlaying one \p AbstractFileSystem on top
Expand Down
1 change: 0 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,6 @@ void AsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const {
}

bool AsmPrinter::doInitialization(Module &M) {
VFS = vfs::getRealFileSystem();
auto *MMIWP = getAnalysisIfAvailable<MachineModuleInfoWrapperPass>();
MMI = MMIWP ? &MMIWP->getMMI() : nullptr;
HasSplitStack = false;
Expand Down
Loading