
Safe Systems Programming in Rust: The Promise and the Challenge Ralf Jung, Jacques-Henri Jourdan, Robbert Krebbers, Derek Dreyer To cite this version: Ralf Jung, Jacques-Henri Jourdan, Robbert Krebbers, Derek Dreyer. Safe Systems Programming in Rust: The Promise and the Challenge. Communications of the ACM, Association for Computing Machinery, 2021, 64 (4), pp.144-152. 10.1145/3418295. hal-03021536 HAL Id: hal-03021536 https://hal.archives-ouvertes.fr/hal-03021536 Submitted on 24 Nov 2020 HAL is a multi-disciplinary open access L’archive ouverte pluridisciplinaire HAL, est archive for the deposit and dissemination of sci- destinée au dépôt et à la diffusion de documents entific research documents, whether they are pub- scientifiques de niveau recherche, publiés ou non, lished or not. The documents may come from émanant des établissements d’enseignement et de teaching and research institutions in France or recherche français ou étrangers, des laboratoires abroad, or from public or private research centers. publics ou privés. Safe Systems Programming in Rust: The Promise and the Challenge Ralf Jung Jacques-Henri Jourdan Robbert Krebbers Derek Dreyer MPI-SWS, Université Paris-Saclay, CNRS, LRI, TU Delft, MPI-SWS, Germany France The Netherlands Germany this control comes at a steep cost. For example, Microsoft Key Insights recently reported that 70% of the security vulnerabilities they • Rust is the first industry-supported programming language fix are due to memory safety violations [33], precisely the to overcome the longstanding tradeoff between the safety type of bugs that strong type systems were designed to rule guarantees of higher-level languages (like Java) and the out. Likewise, Mozilla reports that the vast majority of critical control over resource management provided by lower-level bugs they find in Firefox are memory-related [16]. If only “systems programming” languages (like C and C++). there were a way to somehow get the best of both worlds: a • It tackles this challenge using a strong type system based safe systems programming language with control... on the ideas of ownership and borrowing, which statically Enter Rust. Sponsored by Mozilla and developed actively prohibits the mutation of shared state. This approach en- over the past decade by a large and diverse community of ables many common systems programming pitfalls to be contributors, Rust supports many common low-level program- detected at compile time. ming idioms and APIs derived from modern C++. However, • There are a number of data types whose implementations unlike C++, Rust enforces the safe usage of these APIs with fundamentally depend on shared mutable state and thus a strong static type system. cannot be typechecked according to Rust’s strict ownership In particular, like Java, Rust protects programmers from discipline. To support such data types, Rust embraces the memory safety violations (e.g., “use-after-free” bugs). But judicious use of unsafe code encapsulated within safe APIs. Rust goes further by defending programmers against other, • The proof technique of semantic type soundness, together more insidious anomalies that no other mainstream language with advances in separation logic and machine-checked can prevent. For example, consider data races: unsynchro- proof, has enabled us to begin building rigorous formal nized accesses to shared memory (at least one of which is foundations for Rust as part of the RustBelt project. a write). Even though data races effectively constitute unde- fined (or weakly-defined) behavior for concurrent code, most There is a longstanding tension in programming language “safe” languages (such as Java and Go) permit them, and they design between two seemingly irreconcilable desiderata. are a reliable source of concurrency bugs [35]. In contrast, Rust’s type system rules out data races at compile time. • Safety. We want strong type systems that rule out large Rust has been steadily gaining in popularity, to the point classes of bugs statically. We want automatic memory that it is now being used internally by many major indus- management. We want data encapsulation, so that we can trial software vendors (such as Dropbox, Facebook, Ama- enforce invariants on the private representations of objects zon, and Cloudflare) and has topped StackOverflow’s list and be sure that they will not be broken by untrusted code. of “most loved” programming languages for the past four • Control. At least for “systems programming” applications years. Microsoft’s Security Response Center Team recently like web browsers, operating systems, or game engines, announced that it is actively exploring an investment in the where performance or resource constraints are a primary use of Rust at Microsoft to stem the tide of security vulnera- concern, we want to determine the byte-level representa- bilities in system software [25,8]. tion of data. We want to optimize the time and space usage The design of Rust draws deeply from the wellspring of of our programs using low-level programming techniques. academic research on safe systems programming. In particu- We want access to the “bare metal” when we need it. lar, the most distinctive feature of Rust’s design—in relation to other mainstream languages—is its adoption of an own- Sadly, the conventional wisdom goes, we can’t have ership type system (which in the academic literature is often everything we want. Languages like Java give us strong referred to as an affine or substructural type system [36]). safety, but it comes at the expense of control. As a result, for Ownership type systems help the programmer enforce safe many systems programming applications, the only realistic patterns of lower-level programming by placing restrictions option is to use a language like C or C++ that provides on which aliases (references) to an object may be used to fine-grained control over resource management. However, mutate it at any given point in the program’s execution. 1 2020/5/1 However, Rust goes beyond the ownership type systems still points to the old buffer. In other words, adding a new of prior work in at least two novel and exciting ways: element to v has turned vptr into a dangling pointer. This is possible because both pointers were aliasing: an action 1. Rust employs the mechanisms of borrowing and lifetimes, through a pointer (v) will in general also affect all its aliases which make it much easier to express common C++-style (vptr). The entire situation is visualized as follows: idioms and ensure that they are used safely. v 2. Rust also provides a rich set of APIs—e.g., for concur- 10 10 rency abstractions, efficient data structures, and memory vptr 11 11 management—which fundamentally extend the power of 12 the language by supporting more flexible combinations The fact that vptr is now a dangling pointer becomes of aliasing and mutation than Rust’s core type system al- a problem in the fourth line. Here we load from vptr, and lows. Correspondingly, these APIs cannot be implemented since it is a dangling pointer, this is a use-after-free bug. within the safe fragment of Rust: rather, they internally In fact, the problem is common enough that one instance of make use of potentially unsafe C-style features of the lan- it has its own name: iterator invalidation, which refers to the guage, but in a safely encapsulated way that is claimed situation where an iterator (usually internally implemented not to disturb Rust’s language-level safety guarantees. with a pointer) gets invalidated because the data structure it it- These aspects of Rust’s design are not only essential to erates over is mutated during the iteration. It most commonly its success—they also pose fundamental research questions arises when one iterates over some container data structure in about its semantics and safety guarantees that the program- a loop, and indirectly, but accidentally, calls an operation that ming languages community is just beginning to explore. mutates the data structure. Notice that in practice the call to In this article, we begin by giving the reader a bird’s-eye the operation that mutates the data structure (push_back in view of the Rust programming language, with an emphasis line 3 of our example) might be deeply nested behind several on some of the essential features of Rust that set it apart from layers of abstraction. In particular when code gets refactored its contemporaries. Second, we describe the initial progress or new features get added, it is often near impossible to de- made in the RustBelt project, an ongoing project funded termine if pushing to a certain vector will invalidate pointers by the European Research Council (ERC), whose goal is to elsewhere in the program that are going to be used again later. provide the first formal (and machine-checked) foundations Comparison with garbage-collected languages for the safety claims of Rust. In so doing, we hope to inspire other members of the computer science research community Languages like Java, Go, and OCaml avoid use-after-free to start paying closer attention to Rust and to help contribute bugs using garbage collection: memory is only deallocated to the development of this groundbreaking language. when it can no longer be used by the program. Thus, there can be no dangling pointers and no use-after-free. Motivation: Pointer Invalidation in C++ One problem with garbage collection is that, to make it effi- cient, such languages generally do not permit interior pointers To demonstrate the kind of memory safety problems that (i.e., pointers into data structures). For example, arrays int[] arise commonly in systems programming languages, let us in Java are represented similarly to std::vector<int> in consider the following C++ code: C++ (except arrays in Java cannot be grown). However, un- like in C++, one can only get and set elements of a Java array, 1 std::vector<int>v { 10, 11 }; not take references to them.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages9 Page
-
File Size-