There’s been a bit of noise around on auditing usage of unsafe in Rust dependencies. I’d like to add my thoughts: a review of what unsafe is, why it gives people issues, and what I think a better (but currently non-existent) approach is.
A Brief Rust Primer
Rust is a language that doesn’t use garbage collection. Instead it has the borrow checker, which tracks data access, ownership and lifetimes at compile time, bringing a few benefits:
- You don’t have to manually free objects - the compiler inserts the correct drop (destructor, including freeing heap memory) at compile time
- It prevents memory unsafety - data races, use after free, buffer overflow etc.
Unsafe rust allows a programmer to perform actions the compiler doesn’t know how to verify, with the implication that the programmer knows what he’s doing won’t cause any undefined behavior or illegal memory manipulation. It does this by granting two basic privileges to code marked as unsafe:
- Interact with other unsafe code
- Raw memory manipulation
The compiler assumes that certain situations are impossible, and optimizes accordingly. If the programmer messes up in unsafe, he can introduce undefined behavior or otherwise mess up memory.
If this explanation isn’t satisfying, I suggest checking out What Is Rust’s unsafe?.
Unsafe Rust is essential to Rusts promise of speed, safety and concurrency. Yet because unsafe is inherently more dangerous than other code, it warrants extra caution. Some in the community want to avoid unsafe rust all together in their dependencies. Why?
I think on average we aren’t confident in our ability to audit unsafe code, which paradoxically stems from best practices. The best thing to do with unsafe is to use it to create safe abstractions. That in turn reduces our exposure to unsafe code. The standard library is an excellent example: I consume unsafe code in all of my projects through
Vec (a variable-size, heap-allocated list). I would undoubtedly be better at unsafe if in all my projects I had to re-implement
Vec with the correct usages of unsafe.
Handling Unsafe Ambiguity in Libraries
There are various approaches one can use to handle their lack of unsafe competency when auditing their dependencies. I tried to order them in terms how desireable the approach is. And while it’s up for debate, I think in general 1-2 are things we want, and 3-5 are things we don’t want.
- Become more competent
- Just mimic what other people you trust do
- Use a heuristic to decide what to unsafe to allow in your code (e.g. only allow unsafe for ffi, etc.)
- Don’t review your unsafe code and hope other reviewers have submitted patches for bad unsafe code.
- Don’t use any unsafe (outside of the std library)
A Community Driven Synergistic Solution
I think we should build a site where people can review, annotate and publish their finding on unsafe snippets of code, ideally linked back to the crate (library) and source file from whence it came. It would enable people to mimic others by finding their reviews and deciding based on those reviews whether a piece of unsafe code disqualified the source library as a possible dependency.
Additionally, it would provide a great base for helping people become more competent in much the same manner. You could easily review the annotations of others to learn from them. Additionally, such a platform would be a great tool to use to discover unsafe patterns that are common and help make them more understandable. One could also link in the commits that fix bugs in unsafe code to provide a way to learn about and study common pitfalls in unsafe code.
Ideally said tool would provide programmatic access to make it easier to integrate with existing tooling, like cargo geiger.
Although I talk big, I don’t any plans to create such a tool/site. I hope somebody takes up this idea and runs with it.
In the meantime, I must recommend cargo-geiger. I think most community solutions will in some way utilize the logic if not code in that project.
Appendix - Posts that Influenced this Piece
Thinking of using unsafe? Try this instead. - This is the post that got me really thinking about this. Somebody packaged up an abstraction around some unsafe code, made it very useable and published it. And in the comments people are really happy because this is unsafe code they won’t have to maintain inside their own code base; they can just use the abstraction instead.
Microsoft Security Response Center - Why Rust for safe systems programming - Not really about unsafe per se, but they do list a lack of good unsafe auditing/control techniques as a concern of adopting rust on a wider scale .
The Temptation of Unsafe - When to use unsafe
Observational equivalence and unsafe code - Using unsafe to build fundamental abstractions
cargo-geiger - a tool that lets you count occurrences of unsafe in your crates dependencies