diff --git a/include/swift/AST/PluginLoader.h b/include/swift/AST/PluginLoader.h index 229161515f867..6762c1a0fba44 100644 --- a/include/swift/AST/PluginLoader.h +++ b/include/swift/AST/PluginLoader.h @@ -44,6 +44,7 @@ class PluginLoader { ASTContext &Ctx; DependencyTracker *DepTracker; + const bool disableSandbox; /// Map a module name to an plugin entry that provides the module. llvm::Optional> PluginMap; @@ -52,8 +53,9 @@ class PluginLoader { llvm::DenseMap &getPluginMap(); public: - PluginLoader(ASTContext &Ctx, DependencyTracker *DepTracker) - : Ctx(Ctx), DepTracker(DepTracker) {} + PluginLoader(ASTContext &Ctx, DependencyTracker *DepTracker, + bool disableSandbox = false) + : Ctx(Ctx), DepTracker(DepTracker), disableSandbox(disableSandbox) {} void setRegistry(PluginRegistry *newValue); PluginRegistry *getRegistry(); diff --git a/include/swift/AST/PluginRegistry.h b/include/swift/AST/PluginRegistry.h index ff8406f51b352..307dacd24041b 100644 --- a/include/swift/AST/PluginRegistry.h +++ b/include/swift/AST/PluginRegistry.h @@ -81,6 +81,9 @@ class LoadedExecutablePlugin { /// Callbacks to be called when the connection is restored. llvm::SmallVector *, 0> onReconnect; + /// Disable sandbox. + bool disableSandbox = false; + /// Flag to dump plugin messagings. bool dumpMessaging = false; @@ -91,9 +94,11 @@ class LoadedExecutablePlugin { public: LoadedExecutablePlugin(llvm::StringRef ExecutablePath, - llvm::sys::TimePoint<> LastModificationTime) + llvm::sys::TimePoint<> LastModificationTime, + bool disableSandbox) : ExecutablePath(ExecutablePath), - LastModificationTime(LastModificationTime){}; + LastModificationTime(LastModificationTime), + disableSandbox(disableSandbox){}; ~LoadedExecutablePlugin(); /// The last modification time of 'ExecutablePath' when this object is @@ -173,7 +178,7 @@ class PluginRegistry { /// Load an executable plugin specified by \p path . /// If \p path plugin is already loaded, this returns the cached object. llvm::Expected - loadExecutablePlugin(llvm::StringRef path); + loadExecutablePlugin(llvm::StringRef path, bool disableSandbox); }; } // namespace swift diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index a2f922f308db9..ec7060800fa0d 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -402,6 +402,9 @@ class FrontendOptions { /// are present at LTO time. bool HermeticSealAtLink = false; + /// Disable using the sandbox when executing subprocesses. + bool DisableSandbox = false; + /// The different modes for validating TBD against the LLVM IR. enum class TBDValidationMode { Default, ///< Do the default validation for the current platform. diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 0fe3d042681b2..ea7e653ed2e74 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -1881,4 +1881,9 @@ def load_plugin_executable: "of module names where the macro types are declared">, MetaVarName<"#">; +def disable_sandbox: + Flag<["-"], "disable-sandbox">, + Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>, + HelpText<"Disable using the sandbox when executing subprocesses">; + include "FrontendOptions.td" diff --git a/lib/AST/PluginLoader.cpp b/lib/AST/PluginLoader.cpp index 3a78138fb766f..3836eb61d92af 100644 --- a/lib/AST/PluginLoader.cpp +++ b/lib/AST/PluginLoader.cpp @@ -196,7 +196,8 @@ LoadedExecutablePlugin *PluginLoader::loadExecutablePlugin(StringRef path) { DepTracker->addDependency(resolvedPath, /*IsSystem=*/false); // Load the plugin. - auto plugin = getRegistry()->loadExecutablePlugin(resolvedPath); + auto plugin = + getRegistry()->loadExecutablePlugin(resolvedPath, disableSandbox); if (!plugin) { Ctx.Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, path, llvm::toString(plugin.takeError())); diff --git a/lib/AST/PluginRegistry.cpp b/lib/AST/PluginRegistry.cpp index beceff366b7d2..5eb201a19d5a0 100644 --- a/lib/AST/PluginRegistry.cpp +++ b/lib/AST/PluginRegistry.cpp @@ -84,7 +84,7 @@ void *LoadedLibraryPlugin::getAddressOfSymbol(const char *symbolName) { } llvm::Expected -PluginRegistry::loadExecutablePlugin(StringRef path) { +PluginRegistry::loadExecutablePlugin(StringRef path, bool disableSandbox) { llvm::sys::fs::file_status stat; if (auto err = llvm::sys::fs::status(path, stat)) { return llvm::errorCodeToError(err); @@ -114,7 +114,7 @@ PluginRegistry::loadExecutablePlugin(StringRef path) { } auto plugin = std::make_unique( - path, stat.getLastModificationTime()); + path, stat.getLastModificationTime(), disableSandbox); plugin->setDumpMessaging(dumpMessaging); @@ -147,7 +147,9 @@ llvm::Error LoadedExecutablePlugin::spawnIfNeeded() { // Apply sandboxing. llvm::BumpPtrAllocator Allocator; - Sandbox::apply(command, Allocator); + if (!disableSandbox) { + Sandbox::apply(command, Allocator); + } // Launch. auto childInfo = ExecuteWithPipe(command[0], command); diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 6573d4ff7e248..28bf7927de93a 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -383,6 +383,8 @@ bool ArgsToFrontendOptionsConverter::convert( Opts.BlocklistConfigFilePaths.push_back(A); } + Opts.DisableSandbox = Args.hasArg(OPT_disable_sandbox); + return false; } diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index ffc371ffd54f5..a5e04fe6f21e6 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -792,8 +792,9 @@ bool CompilerInstance::setUpModuleLoaders() { bool CompilerInstance::setUpPluginLoader() { /// FIXME: If Invocation has 'PluginRegistry', we can set it. But should we? - auto loader = - std::make_unique(*Context, getDependencyTracker()); + auto loader = std::make_unique( + *Context, getDependencyTracker(), + Invocation.getFrontendOptions().DisableSandbox); Context->setPluginLoader(std::move(loader)); return false; } diff --git a/test/Macros/macro_plugin_disable_sandbox.swift b/test/Macros/macro_plugin_disable_sandbox.swift new file mode 100644 index 0000000000000..633077786c824 --- /dev/null +++ b/test/Macros/macro_plugin_disable_sandbox.swift @@ -0,0 +1,84 @@ +// REQUIRES: swift_swift_parser + +// sandbox-exec is only avaiable in Darwin +// REQUIRES: OS=macosx + +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/plugins) + +// RUN: split-file %s %t + +//== Build the plugins +// RUN: %host-build-swift \ +// RUN: -swift-version 5 \ +// RUN: -emit-library \ +// RUN: -o %t/plugins/%target-library-name(MacroDefinition) \ +// RUN: -module-name=MacroDefinition \ +// RUN: %t/MacroDefinition.swift \ +// RUN: -g -no-toolchain-stdlib-rpath + +// RUN: %swift-build-cxx-plugin -o %t/mock-plugin %t/TestPlugin.c + +//== Nested sandbox. Expected to fail because sandbox-exec doesn't support nested sandboxing. +// RUN: not sandbox-exec -p '(version 1)(allow default)' \ +// RUN: %swift-target-frontend \ +// RUN: -typecheck -verify \ +// RUN: -swift-version 5 \ +// RUN: -external-plugin-path %t/plugins#%swift-plugin-server \ +// RUN: -load-plugin-executable %t/mock-plugin#TestPlugin \ +// RUN: -module-name MyApp \ +// RUN: %t/test.swift + +//== Avoid nested sandbox by -disable-sandbox +// RUN: sandbox-exec -p '(version 1)(allow default)' \ +// RUN: %swift-target-frontend \ +// RUN: -disable-sandbox \ +// RUN: -typecheck -verify \ +// RUN: -swift-version 5 \ +// RUN: -external-plugin-path %t/plugins#%swift-plugin-server \ +// RUN: -load-plugin-executable %t/mock-plugin#TestPlugin \ +// RUN: -module-name MyApp \ +// RUN: %t/test.swift + + +//--- MacroDefinition.swift +import SwiftSyntax +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +public struct StringifyMacro: ExpressionMacro { + public static func expansion( + of macro: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + guard let argument = macro.arguments.first?.expression else { + fatalError("boom") + } + + return "(\(argument), \(StringLiteralExprSyntax(content: argument.description)))" + } +} + +//--- TestPlugin.c +#include "swift-c/MockPlugin/MockPlugin.h" + +MOCK_PLUGIN([ + { + "expect": {"getCapability": {}}, + "response": {"getCapabilityResult": {"capability": {"protocolVersion": 1}}} + }, + { + "expect": {"expandFreestandingMacro": { + "macro": {"moduleName": "TestPlugin", "typeName": "TestStringMacro"}}}, + "response": {"expandMacroResult": {"expandedSource": "\"test string\"", "diagnostics": []}} + } +]) + +//--- test.swift +@freestanding(expression) macro stringify(_ value: T) -> (T, String) = #externalMacro(module: "MacroDefinition", type: "StringifyMacro") +@freestanding(expression) macro testString() -> String = #externalMacro(module: "TestPlugin", type: "TestStringMacro") + +func test() { + let _: String = #stringify(42).1 + let _: String = #testString +}