Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.10][Macros] Add option to disable sandbox for exectuable plugins #70079

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 4 additions & 2 deletions include/swift/AST/PluginLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<llvm::DenseMap<swift::Identifier, PluginEntry>> PluginMap;
Expand All @@ -52,8 +53,9 @@ class PluginLoader {
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();
Expand Down
11 changes: 8 additions & 3 deletions include/swift/AST/PluginRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,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;

Expand All @@ -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
Expand Down Expand Up @@ -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<LoadedExecutablePlugin *>
loadExecutablePlugin(llvm::StringRef path);
loadExecutablePlugin(llvm::StringRef path, bool disableSandbox);
};

} // namespace swift
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Frontend/FrontendOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1881,4 +1881,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"
3 changes: 2 additions & 1 deletion lib/AST/PluginLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
Expand Down
8 changes: 5 additions & 3 deletions lib/AST/PluginRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -114,7 +114,7 @@ PluginRegistry::loadExecutablePlugin(StringRef path) {
}

auto plugin = std::make_unique<LoadedExecutablePlugin>(
path, stat.getLastModificationTime());
path, stat.getLastModificationTime(), disableSandbox);

plugin->setDumpMessaging(dumpMessaging);

Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions lib/Frontend/ArgsToFrontendOptionsConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,8 @@ bool ArgsToFrontendOptionsConverter::convert(
Opts.BlocklistConfigFilePaths.push_back(A);
}

Opts.DisableSandbox = Args.hasArg(OPT_disable_sandbox);

return false;
}

Expand Down
5 changes: 3 additions & 2 deletions lib/Frontend/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<PluginLoader>(*Context, getDependencyTracker());
auto loader = std::make_unique<PluginLoader>(
*Context, getDependencyTracker(),
Invocation.getFrontendOptions().DisableSandbox);
Context->setPluginLoader(std::move(loader));
return false;
}
Expand Down
84 changes: 84 additions & 0 deletions test/Macros/macro_plugin_disable_sandbox.swift
Original file line number Diff line number Diff line change
@@ -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
}