In this series, we have explored the need for proactive measures to eliminate a class of vulnerabilities and walked through some examples of memory safety issues we’ve found in Microsoft code that could have been avoided with a different language. Now we’ll peek at why we think that Rust represents the best alternative to C and C++ currently available.
First, there are plenty of fantastic memory safe languages already available and widely used inside and outside of Microsoft, including .NET languages like C# or F# and other languages like Swift, Go, and Python. We encourage anyone who is currently using C or C++ to consider whether one of these languages would be appropriate to use instead. We, however, are talking about the need for a safe systems programming language (i.e., a language that can build systems other software runs on, like OS kernels). Such workloads need the speed and predictable performance that C, C++, and Rust provide. Languages which achieve memory safety through garbage collection are not ideal choices for systems programming because their runtimes can lead to unpredictable performance and unnecessary overhead.
Performance and control
When thinking about why Rust is a good alternative, it’s good to think about what we can’t afford to give up by switching from C or C++ — namely performance and control. Rust, just like C and C++ has a minimal and optional “runtime”. Rust’s standard library depends on libc for platforms that support it just like C and C++, but the standard library is also optional so running on platforms without an operating system is also possible.
Rust, just like C and C++, also gives the programmer fine-grained control on when and how much memory is allocated allowing the programmer to have a very good idea of exactly how the program will perform every time it is run. What this means for performance in terms of raw speed, control, and predictability, is that Rust, C, and C++ can be thought of in similar terms.
What separates Rust from C and C++ is its strong safety guarantees. Unless explicitly opted-out of through usage of the “unsafe” keyword, Rust is completely memory safe, meaning that the issues we illustrated in the previous post are impossible to express. In a future post, we’ll revisit those examples to see how Rust prevents those issues usually without adding any runtime overhead. As we’ve seen, roughly 70% of the security issues that the MSRC assigns a CVE to are memory safety issues. This means that if that software had been written in Rust, 70% of these security issues would most likely have been eliminated. And we’re not the only company to have reported such findings.
In systems programming, sometimes the programmer must perform operations that cannot be statically verified as safe. Rust gives the programmer the tools to wrap these operations in safe abstractions, meaning that what was once relegated to code comments or convention can be statically enforced by the Rust compiler. Furthermore, the memory-unsafe operations must be explicitly marked as such, dramatically reducing the surface area security professionals must scrutinize for memory safety vulnerabilities.
It’s not just performance and safety
While Rust initially piqued the interest of the MSRC due to the reasons above, other teams at Microsoft have started adopting Rust for other reasons.
- According to an internal survey, the top reason for adoption was “correctness” – an extension of Rust’s safety guarantees that work towards making true the adage “if it compiles, then it works”.
- Rust statically enforces many properties of a program beyond memory safety, including null pointer safety and data race safety (i.e., no unsynchronized access of a piece of memory from two or more threads).
- Many teams at Microsoft have found that Rust’s rich type system makes writing expressive programs possible. Concepts like enums with associated data and a powerful trait system further enforce Rust’s goal of making programs as bug-free as possible.
- Rust’s existing community is a huge benefit to the language. Much of the power of a language comes from outside of its core through libraries, tooling, and learning materials. While Rust is still a young language, it boasts a healthy ecosystem with an active and open compiler and language development process, and it shows the ability to both promote a strong open source community and to support production users. This gives us more reason to believe the language has a bright future ahead of it.
All of this explains Rust’s track record of having topped Stack Overflow’s list of most loved languages for the past four years. While it’s too early to say what Rust adoption looks like at a scale as large as Microsoft’s engineering organization, early adoption of Rust has generally been very positive.
A bright future
We believe Rust changes the game when it comes to writing safe systems software. Rust provides the performance and control needed to write low-level systems, while empowering software developers to write robust, secure programs.
While researching Rust, we found some issues that gave and continue to give us pause. Some of these concerns include how to regulate the usage of the “unsafe” superset of Rust at scale, lack of first-class interoperability with C++, and interoperability with existing Microsoft tooling. We’ll be blogging about many of these in the future as they do pose challenges for adoption at the scale of Microsoft, and we want to involve the Rust and wider software communities in helping us find solutions that work for everyone.
But we’re excited about the possibilities. While there are many questions still left to figure out about how Rust fits into the overall Microsoft engineering story, we encourage others to join us in taking a serious look at the language for their systems programming needs.
Ryan Levick, Principal Cloud Developer Advocate