Skip to content

Rework the Sandbox enum into a SandboxProfile struct that can separate out point of configuration from point of application #4005

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

Conversation

abertelrud
Copy link
Contributor

@abertelrud abertelrud commented Jan 12, 2022

This change turns the stateless Sandbox enum into a SandboxProfile struct that can be configured and passed around well before being applied, and it makes the configuration more flexible in several regards:

  • there is a list of path rules allowing a mixed order of allow and deny rules
  • the choice of allowing writing to temporary directories is independent of anything other choice
  • the defaults are built into the initializer rather than a separate strictness parameter — this means that they can be queried once the SandboxProfile has been created and are expressed in terms of the choices available to all sandbox profiles.

Motivation

Separating out the point of configuration from the point of application of the sandbox makes it easier to configure.

The ergonomic changes described above also make it easier to use.

Having the sandbox profile be a struct that generates the platform specifics as needed also provides a place with which to associate any cached/compiled representation for profiles that are largely static.

Details

As cleanup, this commit also removes the pre-SwiftPM 5.3 specialities which were specific to running the package manifest in an interpreter rather than compiling and executing it. This functionality is no longer relevant since it isn't possible to run any manifest in the interpreter.

In this commit, the call sites have been adjusted so that they use the modified SandboxProfile API, but still construct the profiles on-the-fly as before. A future change could make them instead be configured at an early point and then applied later when the sandboxed process is actually launched. Another future change could add the sandbox profile to Process as a property so that there is no API assumption that applying a sandbox necessarily involves just modifying the command line.

@abertelrud abertelrud self-assigned this Jan 12, 2022
@abertelrud abertelrud added the ready Author believes the PR is ready to be merged & any feedback has been addressed label Jan 12, 2022
@@ -1361,6 +1370,7 @@ final class PackageToolTests: CommandsTestCase {
let output = try result.utf8Output() + result.utf8stderrOutput()
XCTAssertEqual(result.exitStatus, .terminated(code: 0), "output: \(output)")
XCTAssertMatch(output, .contains("This is MyCommandPlugin."))
XCTAssertMatch(output, .contains("successfully created file at path \(resolveSymlinks(packageDir).appending(components: ".build", "plugins", "outputs", "Foo"))"))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the check that was incomplete in the previous test, and would have caught the problem.

@abertelrud
Copy link
Contributor Author

@swift-ci smoke test

@abertelrud abertelrud force-pushed the eng/87417780-sandbox-should-always-allow-plugin-output-directory branch from 0b631ed to 836aabb Compare January 12, 2022 08:44
@abertelrud
Copy link
Contributor Author

@swift-ci smoke test

}
}

func testWritingToWritableInsideReadOnlyAllowed() throws {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds a specific test case for a writable directory inside of a read-only directory.

Comment on lines 902 to 903
let strictness: Sandbox.Strictness = toolsVersion < .v5_3 ? .manifest_pre_53 : .default
cmd = Sandbox.apply(command: cmd, strictness: strictness, writableDirectories: cacheDirectories)
let sandboxProfile = SandboxProfile(pathRules: cacheDirectories.map{ .writable($0) })
cmd = sandboxProfile.apply(to: cmd)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notes in the Sandbox profile generator indicate that the pre-5.3 rules were needed for interpreting the manifest rather than compiling and running it. This isn't supported anymore, and the operations affected were being able to write to some Clang locations and to set sysctls. Those don't seem like operations that we want to keep supporting. Do we know of any packages that required the < 5.3 strictness?

@abertelrud abertelrud force-pushed the eng/87417780-sandbox-should-always-allow-plugin-output-directory branch from 836aabb to 692e86a Compare January 12, 2022 08:58
@abertelrud
Copy link
Contributor Author

@swift-ci smoke test

Copy link
Contributor

@tomerd tomerd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<3 this is great

@abertelrud
Copy link
Contributor Author

@swift-ci please test package compatibility

@abertelrud
Copy link
Contributor Author

Hmm...

11:01:23 
/Users/buildnode/jenkins/workspace/swift-package-manager-source-compat-suite-PR-macos/swift-driver/Sources/SwiftDriver/Utilities/System.swift:60:32: error: multi-line string literal content must begin on a new line
11:01:23       var quoted: String = #"""#

@abertelrud abertelrud marked this pull request as draft January 13, 2022 00:01
@abertelrud abertelrud changed the title Sandbox blocks output to default plugin output directory when it's under <pkgdir>/.build Rework the Sandbox enum into a SandboxProfile struct that can separate out point of configuration from point of application Jan 13, 2022
@abertelrud
Copy link
Contributor Author

Moving this one aside temporarily while preparing a much smaller fix for the problem at hand. That fix can then be nominated for inclusion in 5.6. Then I'll bring this back since it's what we want to do moving forward.

@abertelrud abertelrud added WIP Work in progress and removed ready Author believes the PR is ready to be merged & any feedback has been addressed labels Jan 13, 2022
@abertelrud
Copy link
Contributor Author

@swift-ci please test package compatibility

@abertelrud abertelrud force-pushed the eng/87417780-sandbox-should-always-allow-plugin-output-directory branch 2 times, most recently from 16e9075 to af586c4 Compare April 19, 2022 08:27
@abertelrud
Copy link
Contributor Author

@swift-ci please smoke test

@abertelrud abertelrud force-pushed the eng/87417780-sandbox-should-always-allow-plugin-output-directory branch from af586c4 to ccdc849 Compare April 19, 2022 17:33
…vide a unique path specified by `TMPDIR`

The `/tmp` temporary directory is particularly unsafe because it is noisy — various processes with various ownerships write and read files there.  So it's better to use the per-user directory returned by `NSTemporaryDirectory()`.

We can improve several things here by passing through the result of calling `NSTemporaryDirectory()` in the sandbox instead of `/tmp` and also setting `TMPDIR` in the environment (regardless of whether or not it was set in the inherited one).  This will cause the inferior's use of Foundation to respect this directory (the implementation of `NSTemporaryDirectory()` looks at `TMPDIR` on all platforms), and that should also be true for any command line tools it calls, as long as it passes through the environment.

If `TMPDIR` is set in SwiftPM's own environment, it will control SwiftPM's own `NSTemporaryDirectory()` will be respected all the way through.

As part of this we also take the opportunity to clean up the interface a little.  In particular, this change turns the stateless `Sandbox` enum into a `SandboxProfile` struct that's a bit more flexible:

- there is a list of path rules allowing a mixed order of `writable`, `readonly`, and `noaccess` rules

- the defaults are built into the initializer rather than a separate `strictness` parameter — this means that they can be queried once the SandboxProfile has been created and are expressed in terms of the choices available to all sandbox profiles

- there is separate control for whether or not to allow network access

The new `SandboxProfile` is also a bit more ergonomic — as a struct, it can be copied, mutated, and carried around in a property before being used.

Having the sandbox profile be a struct that generates the platform specifics as needed also could also provide a place with which to associate any cached/compiled representation for profiles that are largely static, for any platform that supports that.

As cleanup, this commit also removes the pre-SwiftPM 5.3 specialities which were specific to running the package manifest in an interpreter rather than compiling and executing it.  This functionality is no longer relevant since it isn't possible to run any manifest in the interpreter, no matter what tools version the package specifies.

In this commit, the call sites have been adjusted so that they use the modified SandboxProfile API, but they still construct the profiles on-the-fly as before.  A future change will make them instead be configured at an early point and then applied later when the sandboxed process is actually launched.  Another future change could add the sandbox profile to `Process` as a property so that there is no API assumption that applying a sandbox necessarily involves just modifying the command line.

rdar://91575256
@abertelrud abertelrud force-pushed the eng/87417780-sandbox-should-always-allow-plugin-output-directory branch from ccdc849 to e32cba8 Compare April 19, 2022 17:47
@abertelrud
Copy link
Contributor Author

Opened a new PR here: #4307 that replaces this one

@abertelrud abertelrud closed this Apr 19, 2022
@abertelrud abertelrud deleted the eng/87417780-sandbox-should-always-allow-plugin-output-directory branch April 19, 2022 18:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
WIP Work in progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants