Skip to content

Commit 8e999e3

Browse files
authored
[llvm][clang] Sandbox filesystem reads (#165350)
This PR introduces a new mechanism for enforcing a sandbox around filesystem reads coming from the compiler. A fatal error is raised whenever the `llvm::sys::fs`, `llvm::MemoryBuffer::getFile*()` APIs get used directly instead of going through the "blessed" virtual interface of `llvm::vfs::FileSystem`.
1 parent f36792b commit 8e999e3

File tree

28 files changed

+355
-34
lines changed

28 files changed

+355
-34
lines changed

clang/lib/Basic/FileManager.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "llvm/ADT/Statistic.h"
2323
#include "llvm/Config/llvm-config.h"
2424
#include "llvm/Support/FileSystem.h"
25+
#include "llvm/Support/IOSandbox.h"
2526
#include "llvm/Support/MemoryBuffer.h"
2627
#include "llvm/Support/Path.h"
2728
#include "llvm/Support/raw_ostream.h"
@@ -347,12 +348,15 @@ llvm::Expected<FileEntryRef> FileManager::getSTDIN() {
347348
if (STDIN)
348349
return *STDIN;
349350

350-
std::unique_ptr<llvm::MemoryBuffer> Content;
351-
if (auto ContentOrError = llvm::MemoryBuffer::getSTDIN())
352-
Content = std::move(*ContentOrError);
353-
else
351+
auto ContentOrError = [] {
352+
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
353+
return llvm::MemoryBuffer::getSTDIN();
354+
}();
355+
356+
if (!ContentOrError)
354357
return llvm::errorCodeToError(ContentOrError.getError());
355358

359+
auto Content = std::move(*ContentOrError);
356360
STDIN = getVirtualFileRef(Content->getBufferIdentifier(),
357361
Content->getBufferSize(), 0);
358362
FileEntry &FE = const_cast<FileEntry &>(STDIN->getFileEntry());

clang/lib/CodeGen/BackendUtil.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "llvm/Support/BuryPointer.h"
4848
#include "llvm/Support/CommandLine.h"
4949
#include "llvm/Support/Compiler.h"
50+
#include "llvm/Support/IOSandbox.h"
5051
#include "llvm/Support/MemoryBuffer.h"
5152
#include "llvm/Support/PrettyStackTrace.h"
5253
#include "llvm/Support/Program.h"
@@ -1464,6 +1465,8 @@ void clang::emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts,
14641465

14651466
std::unique_ptr<llvm::Module> EmptyModule;
14661467
if (!CGOpts.ThinLTOIndexFile.empty()) {
1468+
// FIXME(sandboxing): Figure out how to support distributed indexing.
1469+
auto BypassSandbox = sys::sandbox::scopedDisable();
14671470
// If we are performing a ThinLTO importing compile, load the function index
14681471
// into memory and pass it into runThinLTOBackend, which will run the
14691472
// function importer and invoke LTO passes.

clang/lib/Driver/Driver.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
#include "llvm/Support/FileSystem.h"
9090
#include "llvm/Support/FileUtilities.h"
9191
#include "llvm/Support/FormatVariadic.h"
92+
#include "llvm/Support/IOSandbox.h"
9293
#include "llvm/Support/MD5.h"
9394
#include "llvm/Support/Path.h"
9495
#include "llvm/Support/PrettyStackTrace.h"
@@ -1848,6 +1849,8 @@ bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename,
18481849
using namespace llvm::sys;
18491850
assert(llvm::Triple(llvm::sys::getProcessTriple()).isOSDarwin() &&
18501851
"Only knows about .crash files on Darwin");
1852+
// This is not a formal output of the compiler, let's bypass the sandbox.
1853+
auto BypassSandbox = sandbox::scopedDisable();
18511854

18521855
// The .crash file can be found on at ~/Library/Logs/DiagnosticReports/
18531856
// (or /Library/Logs/DiagnosticReports for root) and has the filename pattern

clang/lib/Driver/Job.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "llvm/ADT/StringSwitch.h"
2222
#include "llvm/Support/CrashRecoveryContext.h"
2323
#include "llvm/Support/FileSystem.h"
24+
#include "llvm/Support/IOSandbox.h"
2425
#include "llvm/Support/Path.h"
2526
#include "llvm/Support/PrettyStackTrace.h"
2627
#include "llvm/Support/Program.h"
@@ -426,6 +427,10 @@ int CC1Command::Execute(ArrayRef<std::optional<StringRef>> Redirects,
426427
if (ExecutionFailed)
427428
*ExecutionFailed = false;
428429

430+
// Enabling the sandbox here allows us to restore its previous state even when
431+
// this cc1 invocation crashes.
432+
auto EnableSandbox = llvm::sys::sandbox::scopedEnable();
433+
429434
llvm::CrashRecoveryContext CRC;
430435
CRC.DumpStackAndCleanupOnFailure = true;
431436

clang/lib/Serialization/GlobalModuleIndex.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "llvm/Bitstream/BitstreamWriter.h"
2525
#include "llvm/Support/DJB.h"
2626
#include "llvm/Support/FileSystem.h"
27+
#include "llvm/Support/IOSandbox.h"
2728
#include "llvm/Support/LockFileManager.h"
2829
#include "llvm/Support/MemoryBuffer.h"
2930
#include "llvm/Support/OnDiskHashTable.h"
@@ -250,6 +251,9 @@ GlobalModuleIndex::~GlobalModuleIndex() {
250251

251252
std::pair<GlobalModuleIndex *, llvm::Error>
252253
GlobalModuleIndex::readIndex(StringRef Path) {
254+
// This is a compiler-internal input/output, let's bypass the sandbox.
255+
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
256+
253257
// Load the index file, if it's there.
254258
llvm::SmallString<128> IndexPath;
255259
IndexPath += Path;
@@ -843,6 +847,9 @@ llvm::Error
843847
GlobalModuleIndex::writeIndex(FileManager &FileMgr,
844848
const PCHContainerReader &PCHContainerRdr,
845849
StringRef Path) {
850+
// This is a compiler-internal input/output, let's bypass the sandbox.
851+
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
852+
846853
llvm::SmallString<128> IndexPath;
847854
IndexPath += Path;
848855
llvm::sys::path::append(IndexPath, IndexFileName);

clang/lib/Serialization/ModuleCache.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "clang/Serialization/InMemoryModuleCache.h"
1212
#include "clang/Serialization/ModuleFile.h"
1313
#include "llvm/Support/FileSystem.h"
14+
#include "llvm/Support/IOSandbox.h"
1415
#include "llvm/Support/LockFileManager.h"
1516
#include "llvm/Support/Path.h"
1617

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

31+
// This is a compiler-internal input/output, let's bypass the sandbox.
32+
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
33+
3034
llvm::SmallString<128> TimestampFile(Path);
3135
llvm::sys::path::append(TimestampFile, "modules.timestamp");
3236

@@ -103,6 +107,9 @@ class CrossProcessModuleCache : public ModuleCache {
103107

104108
public:
105109
void prepareForGetLock(StringRef ModuleFilename) override {
110+
// This is a compiler-internal input/output, let's bypass the sandbox.
111+
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
112+
106113
// FIXME: Do this in LockFileManager and only if the directory doesn't
107114
// exist.
108115
StringRef Dir = llvm::sys::path::parent_path(ModuleFilename);
@@ -115,6 +122,9 @@ class CrossProcessModuleCache : public ModuleCache {
115122
}
116123

117124
std::time_t getModuleTimestamp(StringRef ModuleFilename) override {
125+
// This is a compiler-internal input/output, let's bypass the sandbox.
126+
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
127+
118128
std::string TimestampFilename =
119129
serialization::ModuleFile::getTimestampFilename(ModuleFilename);
120130
llvm::sys::fs::file_status Status;
@@ -124,6 +134,9 @@ class CrossProcessModuleCache : public ModuleCache {
124134
}
125135

126136
void updateModuleTimestamp(StringRef ModuleFilename) override {
137+
// This is a compiler-internal input/output, let's bypass the sandbox.
138+
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
139+
127140
// Overwrite the timestamp file contents so that file's mtime changes.
128141
std::error_code EC;
129142
llvm::raw_fd_ostream OS(
@@ -138,6 +151,9 @@ class CrossProcessModuleCache : public ModuleCache {
138151

139152
void maybePrune(StringRef Path, time_t PruneInterval,
140153
time_t PruneAfter) override {
154+
// This is a compiler-internal input/output, let's bypass the sandbox.
155+
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
156+
141157
maybePruneImpl(Path, PruneInterval, PruneAfter);
142158
}
143159

clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "llvm/Support/Errc.h"
3838
#include "llvm/Support/ErrorHandling.h"
3939
#include "llvm/Support/FileSystem.h"
40+
#include "llvm/Support/IOSandbox.h"
4041
#include "llvm/Support/Path.h"
4142
#include "llvm/Support/raw_ostream.h"
4243
#include <cassert>
@@ -257,6 +258,9 @@ void HTMLDiagnostics::FlushDiagnosticsImpl(
257258

258259
void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
259260
FilesMade *filesMade) {
261+
// FIXME(sandboxing): Remove this by adopting `llvm::vfs::OutputBackend`.
262+
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
263+
260264
// Create the HTML directory if it is missing.
261265
if (!createdDir) {
262266
createdDir = true;

clang/tools/driver/cc1_main.cpp

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "llvm/Support/BuryPointer.h"
4040
#include "llvm/Support/Compiler.h"
4141
#include "llvm/Support/ErrorHandling.h"
42+
#include "llvm/Support/IOSandbox.h"
4243
#include "llvm/Support/ManagedStatic.h"
4344
#include "llvm/Support/Path.h"
4445
#include "llvm/Support/Process.h"
@@ -273,7 +274,11 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
273274
GetResourcesPath(Argv0, MainAddr);
274275

275276
/// Create the actual file system.
276-
Clang->createVirtualFileSystem(llvm::vfs::getRealFileSystem(), DiagsBuffer);
277+
auto VFS = [] {
278+
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
279+
return llvm::vfs::getRealFileSystem();
280+
}();
281+
Clang->createVirtualFileSystem(std::move(VFS), DiagsBuffer);
277282

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

304309
// If any timers were active but haven't been destroyed yet, print their
305310
// results now. This happens in -disable-free mode.
306-
std::unique_ptr<raw_ostream> IOFile = llvm::CreateInfoOutputFile();
307-
if (Clang->getCodeGenOpts().TimePassesJson) {
308-
*IOFile << "{\n";
309-
llvm::TimerGroup::printAllJSONValues(*IOFile, "");
310-
*IOFile << "\n}\n";
311-
} else if (!Clang->getCodeGenOpts().TimePassesStatsFile) {
312-
llvm::TimerGroup::printAll(*IOFile);
311+
{
312+
// This isn't a formal input or output of the compiler.
313+
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
314+
std::unique_ptr<raw_ostream> IOFile = llvm::CreateInfoOutputFile();
315+
if (Clang->getCodeGenOpts().TimePassesJson) {
316+
*IOFile << "{\n";
317+
llvm::TimerGroup::printAllJSONValues(*IOFile, "");
318+
*IOFile << "\n}\n";
319+
} else if (!Clang->getCodeGenOpts().TimePassesStatsFile) {
320+
llvm::TimerGroup::printAll(*IOFile);
321+
}
322+
llvm::TimerGroup::clearAll();
313323
}
314-
llvm::TimerGroup::clearAll();
315324

316325
if (llvm::timeTraceProfilerEnabled()) {
317326
if (auto profilerOutput = Clang->createOutputFile(

clang/tools/driver/cc1as_main.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "llvm/Support/ErrorHandling.h"
4646
#include "llvm/Support/FileSystem.h"
4747
#include "llvm/Support/FormattedStream.h"
48+
#include "llvm/Support/IOSandbox.h"
4849
#include "llvm/Support/MemoryBuffer.h"
4950
#include "llvm/Support/Path.h"
5051
#include "llvm/Support/Process.h"
@@ -424,8 +425,11 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
424425
if (!TheTarget)
425426
return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple.str();
426427

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

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

674-
auto VFS = vfs::getRealFileSystem();
678+
auto VFS = [] {
679+
auto BypassSandbox = sys::sandbox::scopedDisable();
680+
return vfs::getRealFileSystem();
681+
}();
675682

676683
// Set an error handler, so that any LLVM backend diagnostics go through our
677684
// error handler.

llvm/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,7 @@ else()
704704
option(LLVM_ENABLE_ASSERTIONS "Enable assertions" ON)
705705
endif()
706706

707+
option(LLVM_ENABLE_IO_SANDBOX "Enable IO sandboxing in supported tools" OFF)
707708
option(LLVM_ENABLE_EXPENSIVE_CHECKS "Enable expensive checks" OFF)
708709

709710
set(LLVM_ABI_BREAKING_CHECKS "WITH_ASSERTS" CACHE STRING

0 commit comments

Comments
 (0)