Safe Library Imports

I think there's a lot of confusion, discussion scope creep and talking past each other going on, so let me take a step back and make explicit a bunch of stuff I was probably far too terse or implicit about in my last post (and I think is also being hinted at in everyone else's posts).

Solving Security and "Shooting Down" discussion

Security is hard. There is no One True Threat Model that applies to all users of Rust. There is no single tool, or single feature that can solve all the security issues that matter in practice. Perhaps more importantly, these tools/features/threat models often cannot be discussed in isolation from each other because all these concerns intersect and overlap very heavily.

Like any other challenging problem with no easy answers, there is a long history of people proposing overly simplistic solutions that ignore part of the complexity and in practice would cause more problems than they solve. Explaining why they'd cause more problems than they solve is also very difficult, and it's often impossible to truly convince everyone. In particular, the people suggesting these solutions often get so defensive that they stop thinking about what's actually best for the Rust language and ecosystem in favor of just trying to "win the argument" (this happens on a lot of internals threads, not just security-related ones).

"Mantras" like the ones you listed are what happens when the same argument has to be repeatedly invoked against new equally flawed proposals on a regular basis. The intent is not (or at least it's never supposed to be) about "shooting down" propsoals or "shutting down" discussion, but only to avoid wasting everyone's time rehashing things that "everyone" knows already. Of course, the newcomer making this familiar and flawed proposal is not part of the "everyone" that already knows it's familiar and flawed, which leads to this perception of undeserved hostility.

We've clearly crossed the line where this shorthand is leading to everyone talking past each other instead of avoiding wasted discussion. Which is why I'm about to bite the bullet and rehash some stuff (or to borrow your phrase, do our share of the "emotional labor").

Philosophical Objections

"Demonizing Unsafe"

For historical context, I believe this term originates in [Pre-RFC] Cargo Safety Rails - #25 by nrc and I've probably helped popularize it by using it in posts like [Pre-RFC] Cargo Safety Rails - #51 by Ixrec and Crate capability lists - #15 by Ixrec

Here are some of the key sound bytes:

  • "While I agree that evaluating the quality-level of a program including its dependencies is a good goal, I think focussing on unsafety as a way to do so is somewhat naive."
  • "I believe that the majority of unsafe code is not a problem - it is small, self-contained, and innocuous."
  • "unsafe code is not the only source of bugs. If you care so much about safety/quality that you would check every dependent crate for unsafe code, you should almost certainly be checking them all for logic bugs too."
  • "If we treat known-sound unsafe code differently from safe code, that makes known-sound unsafe code seem more dangerous than it really is, and makes safe code seem more safe than it really is."
  • "there’s a very strong risk of any audit framework/tooling like this unintentionally leading to crate authors being discouraged from using any unsafe code for optimization, even when the soundness of that unsafe code is uncontroversial ... we’d be causing more harm than good if we introduced a system that did have this problem in practice (if nothing else, it risks encouraging the idea that security is at odds with performance)."

As in those threads, I'm assuming it's uncontroversial that there is such a thing as "known-sound unsafe code", that we as a community are capable of identifying it, and that it should not be treated any differently than safe code by the ecosystem (even though the compiler and core language obviously do need to treat it differently).

Hopefully it's now obvious why this is a legitimate objection to the proposal in this thread, just as it was in those past threads. If "safe imports" were added to the language, then it becomes a breaking change for any library to replace safe code with (even known-sound) unsafe code. While I hate to sound like I'm shutting down proposals, we have to be able to express objections to proposal, and I really do believe that would cause far more harm than good.

If anyone can think of a better shorthand for this position than "don't demonize unsafe", I'd love to hear it.

To be super clear, I do think the presence of unsafe is a good heuristic for where to focus personal or community auditing efforts. I also think that the bar for "known-sound" ought to be very high, and I agree that a minority of people in the Rust community can meet that bar. But none of that contradicts the rest of this section.

One True Threat Model

In an even broader sense, the real problem with many of these past threads is that they're effectively proposing that we hardcode one specific threat model into cargo or or (in this case) the Rust language itself.

This is where the mantras of “false sense of security”, “not the real problem”, “static analysis is the answer”, and “trust audits are the answer” come in.

What we should really say is:

  • only the application developer can decide what their threat model should be
  • obviously, there is no single threat model that is correct for all apps all the time
  • less obviously, there is also no good "default" or "lowest common denominator" threat model that is "good enough" for "most" apps
  • we should avoid designs that would give application developers the impression that they don't need to pick a threat model, or that we've solved that problem for them and they don't need to think about what their threat model is
  • we should develop tools and features that enable you to enforce whatever threat model you care about

Or at least that's what I have in mind when I say things like that. I think we all agree that there are important threat models where ruling out file system access, network access or un-audited unsafe code would be extremely valuable, but there are also other important threat models more interested in side channels, timing attacks, DoS attacks and so on. We simply shouldn't hardcode any one of these threat models.

Incomplete/Unclear Proposal

How would we actually use these features?

Another common problem that I think gets in the way of productive discussion is a failure to articulate how security-related proposals for tools or language changes would actually get used in practice. Because of the obvious practical reality that nobody can clean-room reimplement all of their dependencies, or manually audit all of their dependencies, what workflow you have in mind becomes extremely important.

For instance, with the proposal in this thread, the only usage I'm aware of is to simply mark all your imports as "safe", see which ones fail to compile, then remove those "safe" annotations and then audit those crates. That's essentially the same workflow as running a web-of-trust tool on your project to produce a list of dependencies that need auditing. If tools like cargo-crev ranked the un-audited crates by things like "contains unsafe", you get all the same benefits of focusing on more-likely-to-be-dangerous code without "demonizing" any of the crates with known-sound unsafe (assuming we can define "known-sound" in terms of community audits, which is a big assumption).

So in addition to the abstract philosophical objections given above, I'm also just not seeing how a language feature like this would be a practical improvement over an external tool. I think this is a big part of the reason many are responding by saying this solves the wrong problem or other tools do a better job.

Did you have some other workflow in mind? Are there any imports you wouldn't always want to apply "safe" to? Did you want to imbue "safe" with some other semantics beyond sometimes failing compilation? When you need a crate that uses unsafe, would you do something other than audit it for soundness?

Why all the talk of effect systems?

I think this happened because your original post said "they cannot access files or the network", and it's not at all clear how this could possibly be implemented (in a robust enough way to provide any meaningful security guarantees) without a full blown effect system. A more recent post of yours also said "If you can get static assurances from the type and build system, you don’t need trust audits and further static analysis" which raises the same question. And as I was writing this, your new post responding to my accidentally posted incomplete draft of this post is making further claims of this sort.

So while I agree that your original post intended to be a far simpler proposal than an effect system, you've been consistently making claims that conflict with that intent. Importantly, a lot of your responses to the other objections seem to rely on these claims that are hard to make sense of without an effect system. I think this is the biggest reason why so many posts in this specific thread seem to be talking past each other, but it's not really relevant to the broader issues that this thread has in common with many past threads, and that's why this is the shortest section.

Moving Forward

Fundamentally, I believe that "solving security" is too complex of a problem to be solved by simply having people submit proposals and then debating each proposal in isolation the way this forum is currently set up. This is not about whether the "pro-safe import" people "lose" to the "anti-safe import" people; that's completely the wrong way to frame this kind of discussion, yet it is how it gets implicitly framed by the very format of "one user posts a pre-RFC thread, everyone else posts responses to it".

And that is why my first instinct when presented with this thread was to link everyone to the working group. This is exactly the kind of thing working groups are for: Look at a complex problem, discuss a wide range of possible solutions with input from as many interested parties as possible, and decide as a group which solutions are worth pursuing and which aren't, based in part on past experience and which other solutions are out there to avoid redundancy or confusion.

I happen to believe the specific proposal that started this thread is not part of the ideal set of solutions. But I also think that's far less important than the idea that it makes no sense to discuss this specific proposal in isolation. There are absolutely ideas in this proposal that should be investigated thoroughly, and as far as I know they already are being investigated in other ways.

For example, I think we're all interested in ways to express "these dependencies cannot use the network". But we don't seem to have any concrete proposal for how to define that, and some proposals for enforcing it have the obviously problem that they make adding any kind of network feature a backwards-incompatible change. However, perhaps we could:

  • make all crates that access the network on purpose tag themselves in some way
  • get the whole Rust community to agree on a certain tagging system
  • teach web-of-trust tools to show these tags in their output
  • make one of the audit criteria for web-of-trust tools checking that you've been tagged correctly and none of your unsafe code circumvents that and so on
  • state that the intended workflow is not forbidding network usage at compile time but instead a CI job that fails if new network users ever appear in your dependency tree

I think there's probably a viable solution in here. But as you can see, this faint sketch of an idea is already at least five separate proposals, and I believe that's typical of security issues. That's why I think threads discussing single proposals in isolation are not a good way to make progress. If I had more free time or relevant expertise, I'd probably join a working group myself.

I do think that threads discussion problems in isolation might be worthwhile though. For instance, a thread just focused on how to define "network access" could at least work out whether there are any usefully tool-able definitions of that phrase. And personally I'd really like to see a thread about describing people's intended usage of transitive language subsetting features like this; as discussed above the only usage I know of is "audit every crate using X", but that makes a lot of these security-related feature proposals obviously redundant, so they must have something else in mind.