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( diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 602068436101b..9985a0141f65f 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" @@ -715,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()) { @@ -1434,6 +1436,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. + [[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 40ea513e85427..ede5fb0e94423 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. + [[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 715429bcd2096..e52d1069b4209 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. + [[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 1e2272c48bd04..297f5645b1645 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. + [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + // Load the index file, if it's there. llvm::SmallString<128> IndexPath; IndexPath += Path; @@ -843,6 +847,9 @@ llvm::Error GlobalModuleIndex::writeIndex(FileManager &FileMgr, const PCHContainerReader &PCHContainerRdr, StringRef Path) { + // This is a compiler-internal input/output, let's bypass the sandbox. + [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + llvm::SmallString<128> IndexPath; IndexPath += Path; llvm::sys::path::append(IndexPath, IndexFileName); diff --git a/clang/lib/Serialization/ModuleCache.cpp b/clang/lib/Serialization/ModuleCache.cpp index 9850956380423..1ae4d6fdc6247 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. + [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + llvm::SmallString<128> TimestampFile(Path); llvm::sys::path::append(TimestampFile, "modules.timestamp"); @@ -115,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. + [[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 217b853305ed1..b201b644359e9 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`. + [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + // Create the HTML directory if it is missing. if (!createdDir) { createdDir = true; diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp index 49f8843515a35..7da2f95eb5fdc 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 = [] { + [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + return llvm::vfs::getRealFileSystem(); + }(); + Clang->createVirtualFileSystem(std::move(VFS), DiagsBuffer); // Create the actual diagnostics engine. Clang->createDiagnostics(); @@ -302,15 +307,19 @@ int cc1_main(ArrayRef 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. + [[maybe_unused]] auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + 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 50da2f8449a22..eed2bb59f32bf 100644 --- a/clang/tools/driver/cc1as_main.cpp +++ b/clang/tools/driver/cc1as_main.cpp @@ -45,6 +45,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormattedStream.h" +#include "llvm/Support/IOSandbox.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" @@ -425,8 +426,11 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts, if (!TheTarget) return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple.str(); - ErrorOr> Buffer = - MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true); + ErrorOr> Buffer = [=] { + // FIXME(sandboxing): Make this a proper input file. + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); + return MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true); + }(); if (std::error_code EC = Buffer.getError()) { return Diags.Report(diag::err_fe_error_reading) @@ -672,7 +676,10 @@ int cc1as_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { DiagClient->setPrefix("clang -cc1as"); DiagnosticsEngine Diags(DiagnosticIDs::create(), DiagOpts, DiagClient); - auto VFS = vfs::getRealFileSystem(); + auto VFS = [] { + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); + return vfs::getRealFileSystem(); + }(); // Set an error handler, so that any LLVM backend diagnostics go through our // error handler. 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/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h index 9ace2555b4b62..19ca44429af4d 100644 --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -16,7 +16,6 @@ #define LLVM_CODEGEN_ASMPRINTER_H #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" @@ -88,10 +87,6 @@ namespace remarks { class RemarkStreamer; } -namespace vfs { -class FileSystem; -} - /// This class is intended to be used as a driving class for all asm writers. class LLVM_ABI AsmPrinter : public MachineFunctionPass { public: @@ -110,9 +105,6 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass { /// generating (such as the current section etc). std::unique_ptr OutStreamer; - /// The VFS to resolve asm include directives. - IntrusiveRefCntPtr VFS; - /// The current machine function. MachineFunction *MF = nullptr; 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/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/include/llvm/Support/IOSandbox.h b/llvm/include/llvm/Support/IOSandbox.h new file mode 100644 index 0000000000000..e5267fe2b5c39 --- /dev/null +++ b/llvm/include/llvm/Support/IOSandbox.h @@ -0,0 +1,103 @@ +//===- 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 + +// 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::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() { return 0; } +inline int scopedDisable() { return 0; } +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...) 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 { + 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)...); + } +}; + +template constexpr auto interpose(FnTy Fn) { + return Interposed{Fn}; +} +} // namespace llvm::sys::sandbox + +#endif 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; } 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..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" @@ -99,7 +100,11 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI, unsigned BufNum = addInlineAsmDiagBuffer(Str, LocMDNode); SourceMgr &SrcMgr = *MMI->getContext().getInlineSourceManager(); SrcMgr.setIncludeDirs(MCOptions.IASSearchPaths); - SrcMgr.setVirtualFileSystem(VFS); + SrcMgr.setVirtualFileSystem([] { + // FIXME(sandboxing): Propagating vfs::FileSystem here is lots of work. + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); + return vfs::getRealFileSystem(); + }()); std::unique_ptr Parser( createMCAsmParser(SrcMgr, OutContext, *OutStreamer, *MAI, BufNum)); diff --git a/llvm/lib/Support/LockFileManager.cpp b/llvm/lib/Support/LockFileManager.cpp index cdded51108b50..215f2f1d2699d 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) { + [[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. ErrorOr> MBOrErr = @@ -164,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 " + @@ -246,6 +251,8 @@ Expected LockFileManager::tryLock() { } LockFileManager::~LockFileManager() { + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); + if (!std::holds_alternative(Owner)) return; @@ -259,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"); @@ -288,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); } diff --git a/llvm/lib/Support/Signals.cpp b/llvm/lib/Support/Signals.cpp index f8a14a45ddc3e..786c4576f13e3 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. + [[maybe_unused]] auto BypassSandbox = sandbox::scopedDisable(); + for (CallbackAndCookie &RunMe : CallBacksToRun()) { auto Expected = CallbackAndCookie::Status::Initialized; auto Desired = CallbackAndCookie::Status::Executing; diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc index 0d991ead72416..ba99c07f3b463 100644 --- a/llvm/lib/Support/Unix/Path.inc +++ b/llvm/lib/Support/Unix/Path.inc @@ -114,6 +114,32 @@ typedef uint_t uint; #define STATVFS_F_FLAG(vfs) (vfs).f_flags #endif +#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; namespace llvm { @@ -197,7 +223,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 +394,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 +407,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 +428,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 +439,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 +456,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 +469,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 +620,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(); @@ -621,7 +647,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) { @@ -764,13 +790,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 +812,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 +829,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 +839,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 +887,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 +904,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 +932,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 +945,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 +967,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 +1002,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 @@ -1031,9 +1057,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 +1181,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 +1189,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 +1197,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 +1250,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 +1279,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 +1305,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 +1316,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 +1383,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 c754b30d8de4a..0683cf8b3bd1f 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" @@ -221,6 +222,7 @@ RealFile::~RealFile() { close(); } ErrorOr RealFile::status() { assert(FD != kInvalidFile && "cannot stat closed file"); if (!S.isStatusKnown()) { + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); file_status RealStatus; if (std::error_code EC = sys::fs::status(FD, RealStatus)) return EC; @@ -237,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"); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator, IsVolatile); } @@ -306,6 +309,7 @@ class RealFileSystem : public FileSystem { ErrorOr> openFileForReadWithFlags(const Twine &Name, sys::fs::OpenFlags Flags) { + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<256> RealName, Storage; Expected FDOrErr = sys::fs::openNativeFileForRead( adjustPath(Name, Storage), Flags, &RealName); @@ -327,6 +331,7 @@ class RealFileSystem : public FileSystem { } // namespace ErrorOr RealFileSystem::status(const Twine &Path) { + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<256> Storage; sys::fs::file_status RealStatus; if (std::error_code EC = @@ -351,6 +356,7 @@ llvm::ErrorOr RealFileSystem::getCurrentWorkingDirectory() const { if (WD) return WD->getError(); + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<128> Dir; if (std::error_code EC = llvm::sys::fs::current_path(Dir)) return EC; @@ -381,6 +387,7 @@ std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) { std::error_code RealFileSystem::getRealPath(const Twine &Path, SmallVectorImpl &Output) { + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); SmallString<256> Storage; return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output); } @@ -399,10 +406,12 @@ void RealFileSystem::printImpl(raw_ostream &OS, PrintType Type, IntrusiveRefCntPtr vfs::getRealFileSystem() { static IntrusiveRefCntPtr FS = makeIntrusiveRefCnt(true); + sys::sandbox::violationIfEnabled(); return FS; } std::unique_ptr vfs::createPhysicalFileSystem() { + sys::sandbox::violationIfEnabled(); return std::make_unique(false); } @@ -412,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) { + [[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 { + [[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 de59b8ab63a53..90b4c3173b28d 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() { + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); + // 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) { + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); + SmallString<256> AbsPath; if (Path != "-") { AbsPath = Path; 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(); diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp index 07b99896543bd..99ac3e1d75b47 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`. + [[maybe_unused]] auto BypassSandbox = sys::sandbox::scopedDisable(); + if (FD < 0 ) { ShouldClose = false; return; 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)