Rfcs: Sandbox build.rs and plugins

Created on 27 Feb 2016  ·  17Comments  ·  Source: rust-lang/rfcs

Currently, it's impossible to even _typecheck_ a crate without running arbitrary code. Unless I'm mistaken, most plugins/build.rs files just do codegen so they should work just fine inside a sandbox so it would be nice to provide some sort of safe/sandboxed way to generate code. See:

https://github.com/flycheck/flycheck/issues/894

T-dev-tools

Most helpful comment

(trying to resurrect this thread) - without being able to sandbox build.rs, any usage of Rust made by me and my industry is hamstrung due to security concerns. We're effectively in the situation where we cannot use crates from crates.io without hand inspecting every crate/version to ensure that nothing malicious occurs.

While this is not much different from the existing C/C++ community (pull in a repo, use some arbitrary build system that could equally be malicious), the problem in Rust is exacerbated primarily _because_ of how easy/safe it feels to pull in crates from crates.io.

As a strawperson solution:

  • Could we define a 'safe' subset of what is allowed in a build.rs - EG. no file system / network / extern C calls.
  • Could we allow crates to advertise via their TOML that they are a exposing a 'safe' build.rs / or even a 'safe' crate.
  • A safe crate would only allow calls to 'safe' std functions - we could start by allowing 0 functions, and introduce a macro #[allow_in_safe] and gradually roll that out across the std as required. If a build.rs is 'safe' it can only call allow_in_safe functions, if a crate is 'safe' all the rust code can only call allow_in_safe functions.
  • Allow a build.rs to write to a single output while in 'safe' mode. This would still allow nearly all codegen requests with Rust while still sandboxing what can be done.
  • Only allow them to read files within the crates folder in a build.rs while in 'safe' mode.
  • Lastly allow a TOML to specify that it can only use 'safe' build.rs or 'safe' crate, and make it an error if the crate is not 'safe'.
  • Also don't stop build.rs as it is now, but make this a developer opt-in for those crates that are willing to make the change / those crate-users that are most concerned by security.

I may have missed something, but I (and others) have been trying to work out a nice solution to a very real problem with crates.io, and we obviously don't want to lose the awesome power of build.rs (I use it in my own Vulkan bindings!) but want to ensure people see our own crates as being secure.

I'm worried that this issue will be sat on until some utterly horrendous backdoor is discovered in a crate that malwares up a system or copies the contents elsewhere, and the backlash against our community will be harsher than would be warranted.

I really want a solution here 😄

All 17 comments

:+1:, I wasn't aware this wasn't already done.

What kind of sandbox? What would it disallow? How would buildscripts that need extra permissions ask for them?

@sfackler I imagine two ways of doing it:

1) Using a restricting virtual environment.
2) Restricting the code to a subset of Rust.

The former is probably the easiest. However, the latter seems like the "correct" solution.

Personally, I'd go with 1. On Linux, I'd use seccomp and pass in a file descriptor to which generated code should be written.

Unfortunately, this won't work for projects that need to build c code.

I actually think (2) seems better, and I'm not sure I'd even go with Rust. It's good to have general-purpose build.rs for exceptional situations, but I bet most current ones fall into several cookie-cutter categories:

  1. Build a simple C library and link it in (e.g. a -sys crate)
  2. Syntex stuff
  3. Output some custom linker flags (same ones every time)

Would it be good to have a simple configuration format describing these? It could also be used to make decisions like @Stebalien says, i.e. Syntex munging needs to be done before typechecking but linker stuff does not. It also plays into the question of reproducible builds.

(trying to resurrect this thread) - without being able to sandbox build.rs, any usage of Rust made by me and my industry is hamstrung due to security concerns. We're effectively in the situation where we cannot use crates from crates.io without hand inspecting every crate/version to ensure that nothing malicious occurs.

While this is not much different from the existing C/C++ community (pull in a repo, use some arbitrary build system that could equally be malicious), the problem in Rust is exacerbated primarily _because_ of how easy/safe it feels to pull in crates from crates.io.

As a strawperson solution:

  • Could we define a 'safe' subset of what is allowed in a build.rs - EG. no file system / network / extern C calls.
  • Could we allow crates to advertise via their TOML that they are a exposing a 'safe' build.rs / or even a 'safe' crate.
  • A safe crate would only allow calls to 'safe' std functions - we could start by allowing 0 functions, and introduce a macro #[allow_in_safe] and gradually roll that out across the std as required. If a build.rs is 'safe' it can only call allow_in_safe functions, if a crate is 'safe' all the rust code can only call allow_in_safe functions.
  • Allow a build.rs to write to a single output while in 'safe' mode. This would still allow nearly all codegen requests with Rust while still sandboxing what can be done.
  • Only allow them to read files within the crates folder in a build.rs while in 'safe' mode.
  • Lastly allow a TOML to specify that it can only use 'safe' build.rs or 'safe' crate, and make it an error if the crate is not 'safe'.
  • Also don't stop build.rs as it is now, but make this a developer opt-in for those crates that are willing to make the change / those crate-users that are most concerned by security.

I may have missed something, but I (and others) have been trying to work out a nice solution to a very real problem with crates.io, and we obviously don't want to lose the awesome power of build.rs (I use it in my own Vulkan bindings!) but want to ensure people see our own crates as being secure.

I'm worried that this issue will be sat on until some utterly horrendous backdoor is discovered in a crate that malwares up a system or copies the contents elsewhere, and the backlash against our community will be harsher than would be warranted.

I really want a solution here 😄

Don't procedural macros also allow arbitrary code execution? So it would not be sufficient to sandbox build.rs. I also think that most crates with a build.rs will do file system access (particularly now that procedural macros exist) and so providing a "safe" language subset will not be particularly useful, whilst also leaving a very large attack surface.

These seem like the most promising options:

  • Using existing sand-boxing technologies, maybe there's even a standardised interface for this, so that the sand-boxing technology can be swapped out easily?
  • Using something like MIRI or a wasm interpreter to interpret code run at compile time, and limit access that way.
  • Provide a way to disable compile-time execution entirely for most crates, and then have a white-list of "safe" crates. Perhaps there could be a community-maintained list that companies could use, so that the most common crates like serde, etc. can be relied on without additional work.

Most crates that use build.rs seem to read some files within the crate, and generate a single generated.rs - all contained by reads from the crate and writes only to the output build folder. This seems like it works in a sandbox model.

Having some way to say 'a crate is safe' is great! Have it be a badge on crates.io, and allow you to when specifying the dependencies in cargo.toml say 'use this crate at this version but only if it is safe'.

Honestly using wasm or something like that seems entirely doable - I don't really care whether my solution is the right one, only that we can start having some mechanism to provide guarantees here!

Perhaps an easier starting point would be to allow a cargo build --docker or something, which would do the entire build in a container?

@mark-i-m there are already docker images for this - I use https://hub.docker.com/r/ekidd/rust-musl-builder/ at work, and it's pretty easy already, even without any cargo-specific functionality. I think if you're going to go that route it's going to be easier to just continue using docker directly.

@Diggsey That's fair, but my point is that if this is a barrier for more people, then perhaps this is an easy-to-implement-and-maintain sandboxing solution. In other words, the cargo integration makes it convenient to do the secure thing by default.

Maybe we could sandbox macros and build.rs using WebAssembly -it's intended to be sandboxed anyway, and forcing the build tooling to use it would bring development synergy for the emerging ecosystem of WASM-compilant libraries.

This would mean integrating a WebAssembly interpreter in cargo. Possible, but will require quite some work.

I'm unclear on what the precise blocking issues are supposed to be here. In particular, why is "do all your Rust builds in docker" not a solution? Don't you already have to do all your C/C++ builds inside something like that if your threat model includes third-party build scripts?

I do support finding common build.rs patterns and breaking them out into more declarative cargo features, but to me that seems more like an issue of ergonomics, modularity and static analyzability. It definitely can also lead to good heuristics for helping the less paranoid among us stay secure by default (though I have trouble imagining what kinds of attacks we could actually prevent this way). But for an industry that's nervous about using Rust at all because of this, I'm not seeing how making build.rs scripts less common or more restricted (I assume we can't kill them off completely) leads to a meaningful security benefit that they can't already get much more easily and thoroughly with an unrelated-to-Rust sandbox. That gives you total control over what you think should be "safe" for a build.

I'm unclear on what the precise blocking issues are supposed to be here. In particular, why is "do all your Rust builds in docker" not a solution? Don't you already have to do all your C/C++ builds inside something like that if your threat model includes third-party build scripts?

Right - we do not use 3rd party C/C++ libraries because of this reason. What will happen in practice is that any use of Rust that we do will have to have 3rd party extern crates banned because of security concerns. Our legal/security teams are not going to vet each version of each crate.

My hope is that if we at least had a 'safe' crate-badge on crates.io + a toml flag that banned the use of a build.rs - that is a much more palatable crate with which I could convince security + legal people to allow.

And as a further (future) step - some kind of sandboxing of the build.rs such that it cannot just read the entire filesystem and post its contents on the internet would be much welcomed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

camden-smallwood-zz picture camden-smallwood-zz  ·  3Comments

rudolfschmidt picture rudolfschmidt  ·  3Comments

torkleyy picture torkleyy  ·  3Comments

silversolver1 picture silversolver1  ·  3Comments

3442853561 picture 3442853561  ·  3Comments