One of the coolest things about the Rust typesystem is that you can use it to make unsafe bindings safe. Read all about it in the Rustonomicon. However, it can be really quite easy to slip in a bug where you’re not actually making the guarantees you think you’re making. For example, here’s a real bug I made in the ZeroMQ FFI bindings (which have been edited for clarity):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
Here’s the bug if you missed my callout:
1
|
|
My intention was to tie the lifetime of PollItem<'a>
to the lifetime of the
Socket
, but because I left out one measly 'a
, Rust doesn’t tie the two
together, and instead is actually using the 'static
lifetime. This then lets
you do something evil like:
1 2 3 4 5 6 7 8 9 |
|
It’s just that easy. Fix is simple, just change the function to use &'a self
and Rust will refuse to compile this snippet. Job well done!
Well, no, not really. Because what was particularly devious about this bug is
that it actually came back. Later on I accidentally reverted &'a self
back to
&self
because I secretly hate myself. The project and examples still compiled
and ran, but that unitialized dereference was just waiting around to cause a
security vulnerability.
Oops.
Crap.
Making sure Rust actually rejects programs that it ought to be rejecting fundamentally important when writing a library that uses Unsafe Rust.
That’s where compiletest comes in.
It’s a testing framework that’s been extacted from
rust-lang/rust
that lets you write these “shouldn’t-compile” tests. Here’s how to use it.
First add this to your Cargo.toml
. We do a little feature dance because
currently compiletest
only runs on nightly:
1 2 3 4 5 6 7 8 |
|
Then, add add a test driver tests/compile-tests.rs
(or whatever you want to
name it) that runs the compiletest tests:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
Finally, add the test! Here’s the one I wrote, tests/compile-fail/no-leaking-poll-items.rs
:
1 2 3 4 5 6 7 8 9 |
|
Now you can live in peace with the confidence that this bug won’t ever appear again:
1 2 3 4 5 6 7 8 9 10 11 |
|
In summary, use compiletest
, and demand it’s use from the Unsafe Rust
libraries you use! Otherwise you can never be sure if unsafe and undefined
behavior like this will sneak into your project.
TLDR: