mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[Macros] Add option to disable sandbox for exectuable plugins
`-disable-sandbox` to disable sandboxing when invoking subprocess from from the frontend. Since `sandbox(7)` in macOS doesn't support nested sandbox, complation used to fail when the parent build process is sandboxed.
This commit is contained in:
@@ -44,6 +44,7 @@ private:
|
||||
|
||||
ASTContext &Ctx;
|
||||
DependencyTracker *DepTracker;
|
||||
const bool disableSandbox;
|
||||
|
||||
/// Map a module name to an plugin entry that provides the module.
|
||||
llvm::Optional<llvm::DenseMap<swift::Identifier, PluginEntry>> PluginMap;
|
||||
@@ -52,8 +53,9 @@ private:
|
||||
llvm::DenseMap<swift::Identifier, PluginEntry> &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();
|
||||
|
||||
@@ -89,6 +89,9 @@ class LoadedExecutablePlugin {
|
||||
/// Callbacks to be called when the connection is restored.
|
||||
llvm::SmallVector<std::function<void(void)> *, 0> onReconnect;
|
||||
|
||||
/// Disable sandbox.
|
||||
bool disableSandbox = false;
|
||||
|
||||
/// Flag to dump plugin messagings.
|
||||
bool dumpMessaging = false;
|
||||
|
||||
@@ -99,9 +102,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
|
||||
@@ -181,7 +186,7 @@ public:
|
||||
/// Load an executable plugin specified by \p path .
|
||||
/// If \p path plugin is already loaded, this returns the cached object.
|
||||
llvm::Expected<LoadedExecutablePlugin *>
|
||||
loadExecutablePlugin(llvm::StringRef path);
|
||||
loadExecutablePlugin(llvm::StringRef path, bool disableSandbox);
|
||||
};
|
||||
|
||||
} // namespace swift
|
||||
|
||||
@@ -415,6 +415,9 @@ public:
|
||||
/// 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.
|
||||
|
||||
@@ -1948,4 +1948,9 @@ def load_plugin_executable:
|
||||
"of module names where the macro types are declared">,
|
||||
MetaVarName<"<path>#<module-names>">;
|
||||
|
||||
def disable_sandbox:
|
||||
Flag<["-"], "disable-sandbox">,
|
||||
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>,
|
||||
HelpText<"Disable using the sandbox when executing subprocesses">;
|
||||
|
||||
include "FrontendOptions.td"
|
||||
|
||||
@@ -199,7 +199,8 @@ 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) {
|
||||
resolvedPath.push_back(0);
|
||||
return llvm::handleErrors(
|
||||
|
||||
@@ -84,7 +84,7 @@ void *LoadedLibraryPlugin::getAddressOfSymbol(const char *symbolName) {
|
||||
}
|
||||
|
||||
llvm::Expected<LoadedExecutablePlugin *>
|
||||
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<LoadedExecutablePlugin>(
|
||||
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);
|
||||
|
||||
@@ -395,6 +395,8 @@ bool ArgsToFrontendOptionsConverter::convert(
|
||||
Opts.UseCASBackend = Args.hasArg(OPT_cas_backend);
|
||||
Opts.EmitCASIDFile = Args.hasArg(OPT_cas_emit_casid_file);
|
||||
|
||||
Opts.DisableSandbox = Args.hasArg(OPT_disable_sandbox);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -812,8 +812,9 @@ bool CompilerInstance::setUpModuleLoaders() {
|
||||
|
||||
bool CompilerInstance::setUpPluginLoader() {
|
||||
/// FIXME: If Invocation has 'PluginRegistry', we can set it. But should we?
|
||||
auto loader =
|
||||
std::make_unique<PluginLoader>(*Context, getDependencyTracker());
|
||||
auto loader = std::make_unique<PluginLoader>(
|
||||
*Context, getDependencyTracker(),
|
||||
Invocation.getFrontendOptions().DisableSandbox);
|
||||
Context->setPluginLoader(std::move(loader));
|
||||
return false;
|
||||
}
|
||||
|
||||
84
test/Macros/macro_plugin_disable_sandbox.swift
Normal file
84
test/Macros/macro_plugin_disable_sandbox.swift
Normal file
@@ -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<T>(_ 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
|
||||
}
|
||||
Reference in New Issue
Block a user