Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
db5971b
[Support] Introduce IO sandboxing
jansvoboda11 Oct 27, 2025
bafb889
[clang] Enable IO sandboxing in -cc1 and -cc1as
jansvoboda11 Oct 27, 2025
a2e9ca8
[Support] Disallow `MemoryBuffer::getFile*()` in IO sandbox
jansvoboda11 Oct 27, 2025
18d626f
[Support] Disallow `vfs::{create,get}RealFileSystem()` in IO sandbox
jansvoboda11 Oct 27, 2025
77c35cb
[Support] Disallow input `sys::fs` APIs in IO sandbox
jansvoboda11 Oct 27, 2025
40f4307
[Support] Bless `vfs::RealFileSystem` and friends in IO sandbox
jansvoboda11 Oct 27, 2025
24493d7
[Support] Allow `LockFileManager` in IO sandbox
jansvoboda11 Oct 27, 2025
7af44db
[Support] Allow `raw_fd_ostream` in IO sandbox
jansvoboda11 Oct 27, 2025
5b2f160
[Support] Allow `OnDiskOutputBackend` in IO sandbox
jansvoboda11 Oct 27, 2025
a8162da
[clang] Allow `ModuleCache` and `GlobalModuleIndex` in IO sandbox
jansvoboda11 Oct 27, 2025
ad82967
[clang] Allow `HTMLDiagnostics` in IO sandbox
jansvoboda11 Oct 27, 2025
56fda7e
[clang] Allow ThinLTO indexing in IO sandbox
jansvoboda11 Oct 27, 2025
92d60bb
[clang] Allow crash diagnostic file in IO sandbox
jansvoboda11 Oct 27, 2025
37afa44
[CodeGen] Allow `AsmPrinter` in IO sandbox
jansvoboda11 Oct 28, 2025
506bc47
[Target] Allow `BTFDebug` in IO sandbox
jansvoboda11 Oct 28, 2025
b6b9739
[clang] Allow `FileManager` to read stdin
jansvoboda11 Oct 28, 2025
8e49476
[REMOVE] Force-enable IO sandboxing
jansvoboda11 Oct 31, 2025
276dd08
Make use of `LLVM_THREAD_LOCAL`
jansvoboda11 Nov 8, 2025
8929850
Drop bypass from `CrossProcessModuleCache::getLock()`
jansvoboda11 Nov 8, 2025
b71e695
Sink `directory_iterator` violations from header into the implementat…
jansvoboda11 Nov 8, 2025
3a5a95c
Document sandbox on `getRealFileSystem()` and `createPhysicalFileSyst…
jansvoboda11 Nov 8, 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 @@ -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"
Expand Down Expand Up @@ -1442,6 +1443,8 @@ void clang::emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts,

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

// The .crash file can be found on at ~/Library/Logs/DiagnosticReports/
// (or /Library/Logs/DiagnosticReports for root) and has the filename pattern
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
19 changes: 19 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 @@ -111,10 +118,16 @@ class CrossProcessModuleCache : public ModuleCache {

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

return std::make_unique<llvm::LockFileManager>(ModuleFilename);
}

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

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

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

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

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

maybePruneImpl(Path, PruneInterval, PruneAfter);
}

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 @@ -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"
Expand Down Expand Up @@ -272,7 +273,11 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
CompilerInvocation::GetResourcesPath(Argv0, MainAddr);

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

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

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

if (llvm::timeTraceProfilerEnabled()) {
if (auto profilerOutput = Clang->createOutputFile(
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 @@ -425,8 +426,11 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
if (!TheTarget)
return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple.str();

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

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

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

// Set an error handler, so that any LLVM backend diagnostics go through our
// error handler.
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" ON)
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/SmallSet.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
11 changes: 11 additions & 0 deletions llvm/include/llvm/Support/FileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem/UniqueID.h"
#include "llvm/Support/IOSandbox.h"
#include "llvm/Support/MD5.h"
#include <cassert>
#include <cstdint>
Expand Down Expand Up @@ -1447,6 +1448,8 @@ class directory_iterator {
explicit directory_iterator(const Twine &path, std::error_code &ec,
bool follow_symlinks = true)
: FollowSymlinks(follow_symlinks) {
sandbox::violationIfEnabled();

State = std::make_shared<detail::DirIterState>();
SmallString<128> path_storage;
ec = detail::directory_iterator_construct(
Expand All @@ -1456,6 +1459,8 @@ class directory_iterator {
explicit directory_iterator(const directory_entry &de, std::error_code &ec,
bool follow_symlinks = true)
: FollowSymlinks(follow_symlinks) {
sandbox::violationIfEnabled();

State = std::make_shared<detail::DirIterState>();
ec = detail::directory_iterator_construct(
*State, de.path(), FollowSymlinks);
Expand All @@ -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;
}
Expand Down Expand Up @@ -1511,13 +1518,17 @@ class recursive_directory_iterator {
bool follow_symlinks = true)
: State(std::make_shared<detail::RecDirIterState>()),
Follow(follow_symlinks) {
sandbox::violationIfEnabled();

State->Stack.push_back(directory_iterator(path, ec, Follow));
if (State->Stack.back() == directory_iterator())
State.reset();
}

// No operator++ because we need error_code.
recursive_directory_iterator &increment(std::error_code &ec) {
sandbox::violationIfEnabled();

const directory_iterator end_itr = {};

if (State->HasNoPushRequest)
Expand Down
Loading