2 title: The scope of unsafe
3 categories: research rust
6 I'd like to talk about an important aspect of dealing with unsafe code, that still regularly seems to catch people on the wrong foot:
8 > *When checking unsafe code, it is not enough to just check the contents of every `unsafe` block.*
10 It turns out that the underlying reason for this observation is also a nice illustration for the concept of *semantic* types that comes up in my [work on formalizing Rust]({{ site.baseurl }}{% post_url 2015-10-12-formalizing-rust %}) (or rather, its type system).
11 Finally, this discussion will once again lead us to realize that we rely on our type systems to provide much more than just type safety.
17 Before we dive into the deeper reasons for why we have to check (seemingly) safe code, let me start by convincing your that the above statement is actually true.
19 Consider the type `Vec`, which is roughly defined as follows:
27 (I know this definition is not entirely right. If you want to know how the actual `Vec` works, check out the [corresponding section of the Rustonomicon](https://doc.rust-lang.org/nightly/nomicon/vec.html).)
29 Roughly speaking, `ptr` points to the heap-allocated block of memory holding the contents of the vector, `cap` holds the size of that memory block (counted in number of `T`), and `len` stores how many elements are actually stored in the vector right now.
30 `cap` and `len` can be different if the memory block contains some extra (uninitialized) space beyond the end of the vector, which speeds up pushing an element to the end of the vector (at the cost of some extra memory usage).
32 It is very easy to add a function to `Vec` that contains no `unsafe` code, and still breaks the safety of the data structure:
42 We can now "access" two more elements of the vector that have never been added!
43 This means we will read from uninitialized memory, or worse, from unallocated memory.
46 So, this example clearly shows that to evaluate the safety of types like `Vec`, we have to look at *every single function* provided by that data structure, even if it does not contain any `unsafe` code.
50 So, why is it the case that a safe function can break `Vec`?
51 How can we even say that it is the safe function which is at fault, rather than some piece of `unsafe` code elsewhere in `Vec`?
53 The intuitive answer most people will give is that `Vec` has *additional invariants* on its fields.
54 For example, `cap` must be greater than or equal to `len`.
55 More precisely speaking, `ptr` points to an array of type `T` and size `cap`, of which the first `len` elements have already been initialized.
56 The function `evil` above violates this invariant, while all the functions actually provided by `Vec` (including the ones that are implemented unsafely) preserve the invariant.
57 That's why `evil` is the bad guy. (The name kind of gave it away, didn't it?)
59 This may seem obvious in hindsight, but I think it is actually fairly subtle.
60 There used to be claims on the interwebs that "if a Rust program crashes, the bug must be in some `unsafe` block". (And there probably still are.)
61 Even academic researchers working on Rust got this wrong, arguing that in order to detect bugs in data structures like `Vec` it suffices to check functions involving unsafe code.
63 ## The semantic perspective
65 There is another way to phrase the intuition of types having additional invariants:
66 Imagine we define another type in our Rust program:
68 pub struct MyType<T> {
74 We will define only one function for this type:
82 This type *looks* exactly like `Vec`, doesn't it?
83 The two types are *syntactically* equal, and the same goes for the two `evil` functions.
84 Still, `MyType::evil` is a perfeclty benign function (despite its name).
87 Remember that in a [previous blog post]({{ site.baseurl }}{% post_url 2015-10-12-formalizing-rust %}), I argued that types have a *semantic* aspect.
88 For example, a function is semantically well-typed if it *behaves* properly on all valid arguments, independently of how, *syntactically*, the function body has been written down.
90 Semantically speaking, `Vec` is very different from `MyType`:
91 If I have a `Vec`, I actually know that `ptr` is valid, that `len <= cap`, and so on.
92 I know nothing like that about an arbitrary instance of `MyType`: All I know here is that `cap` and `len` are valid elements of `usize`.
93 In other words, the additional invariants that we associate with `Vec` actually make this an *entirely different type*.
94 In order to formally describe what it means to be a `Vec`, we can't just say "well, that's a `struct` with these three fields".
95 That would be *wrong*.
96 But still, that's actually all the Rust compiler knows, and it is all that it actually checks:
97 When type checking `Vec::evil`, the compiler *thinks* that `Vec` is like `MyType`, and under that assumption, it deems `evil` a good function.
98 (Clearly, the compiler did not watch enough movies with serious villains.)
100 In other words, we have to look beyond the mere syntactic appearance of a type, and into its deeper, semantic meaning.
101 As part of my work on formalizing Rust, I will eventually want to prove the soundness of types like `Vec` or `RefCell` and their associated operations.
102 In order to do so, I will first have to figure out what exactly the semantics of these types are. (This will be fairly easy for `Vec`, but really hard for `RefCell`. Can you guess why?)
103 Then I will have to check *every* function operating on these types and prove that they are actually well-typed.
104 Even if the Rust compiler says "yup, that function over there is fine, I checked every single statement of it", that's not enough because the Rust compiler has no idea about which types these functions are *actually* about.
105 All it sees is the syntactic surface.
106 It would then turn out, for example, that `Vec::evil` is actually not a semantically well-typed function. It may leave an invalid `Vec` in `self`.
107 This is just as bad as a function assigning `*b = 42` to a `b: &mut bool`.
108 The only difference is that the compiler will catch the second example since it knows all about the semantics of `bool`. It doesn't know enough about the semantics of `Vec`.
110 ## The actual scope of unsafe
112 At this point, you may be slightly worried about the safety of the Rust ecosystem.
113 I just spent two sections arguing that the Rust compiler actually dosn't know what it is doing when it comes to checking functions that work on `Vec`.
114 How does it come that people carelessly use `Vec` in their programs without causing havoc all the time?
115 Or, to put it slightly different: If the "scope" of `unsafe` (i.e., the code that is potentially "infected" and has to be checked manually) grows beyond the syntactc `unsafe` blocks, then how far does it reach?
116 Does it sprawl through all our code, or is there some limit to its effect?
118 As you probably imagined, of course there *is* a limit: The scope of `unsafe` ends at the next *abstraction boundary*.
119 This means that everything outside of the `std::vec` module does not have to worry about `Vec`.
120 Due to the privacy rules enforces by the compiler, code outside of that module cannot access the private fields of `Vec`, and hence it cannot tell the difference between the syntactic appearance of `Vec` and its actual, semantic meaning.
121 Of course, this also means that *everything* inside `std::vec` is potentially dangerous and needs to be proven to respect the semantics of `Vec`.
123 ## Abstraction Safety
125 This nicely brings us to another important point, which I can only glimpse at here:
127 > *The purpose of type systems goes far beyond type safety: They (also) serve to establish safe abstractions.*
129 If the type system of Rust lacked a mechanism to establish abstraction (i.e., if there were no private fields), type safety would not be affected.
130 However, it would be very dangerous to write a type like `Vec` that has a semantic meaning beyond its syntactic appearance:
131 Since users of `Vec` can accidentally perform invalid operations, there is actually *no bound to the scope of `unsafe`*.
132 To formally establish safety, one would have to literally go over the entire program and prove that it doesn't misuse `Vec`.
133 The safety promise of Rust would be pretty much useless.
135 This should not be entirely surprising if you read my previous [post about formalizing Rust's type system]({{ site.baseurl }}{% post_url 2015-10-12-formalizing-rust %}), where I already argued that a proof of (syntactic) type safety does not help to justify safety of most Rust programs out there.
136 I am now making a similar point, coming from a different angle.
138 The fact that Rust programers *can* use `Vec` and many other types without much care is a property of the type system that is independent of type safety.
139 We may call it *abstraction safety*.
140 Many type systems have this property, just think of `private` fields in Java or C++, or opaque signatures in ML.
141 However, few languages embrace using unsafe primitives and encapsulating them behind an abstraction as much as Rust does.
142 In some sense, Rust relies way more on abstraction safety than most languages - though in practice, programmers of all languages rely on abstraction safety a lot, if it is available.
143 (Most scripting languages, I am looking at you.)
145 Abstraction safety is a very deep point that deserves its own blog post, I only skimmed the surface.
146 Watch this space, a link to such a post will eventually appear. (And I hope I don't have to write it myself.)