This may be part of a collaborative blog post series about underhanded Rust code. Or it may not. I invite you to write your own posts about underhanded code to make it so!
Last month we opened up The Underhanded Rust competition. This contest is about writing seemingly-innocuous malicious code; code that is deliberately written to do some harm, but will pass a typical code review.
It is inspired by the Underhanded C contest. Most of the underhanded C submissions have to do with hidden buffer overflows, pointer arithmetic fails, or misuse of C macros; and these problems largely don’t occur in Rust programs. However, the ability to layer abstractions on each other does open up new avenues to introducing underhandedness by relying on sufficiently confusing abstraction sandwiches. There are probably other interesting avenues. Overall, I’m pretty excited to see what kind of underhandedness folks come up with!
Of course, underhandedness is not just about fun and games; we should be hardening our code against this kind of thing. Even if you trust your fellow programmers. Even if you are the sole programmer and you trust yourself. After all, you can’t spell Trust without Rust; and Rust is indeed about trust. Specifically, Rust is about trusting nobody. Not even yourself.
Rust protects you from your own mistakes when it comes to memory management. But we should be worried about other kinds of mistakes, too. Many of the techniques used in underhanded programming involve sleights of hand that could just as well be introduced in the code by accident, causing bugs. Not memory safety bugs (in Rust), but still, bugs. The existence of these sleights of hand is great for that very common situation when you are feeling severely under-plushied and must win a competition to replenish your supply but we really don’t want these creeping into real-world code, either by accident or intentionally.
Allow me to take a moment out of your busy underhanded-submission-writing schedules to talk to you about our Lord and Savior Clippy.
Clippy is for those of you who have become desensitized to the constant whining of the Rust compiler and need a higher dosage of whininess to be kept on their toes. Clippy is for those perfectionists amongst you who want to know every minute thing wrong with their code so that they can fix it. But really, Clippy is for everyone.
Clippy is simply a large repository of lints. As of the time of writing this post, there are
183 lints in it, though not all of them are enabled by default. These use the regular Rust lint
system so you can pick and choose the ones you need via
#[warn(lint_name)]. These lints cover a wide range of functions:
- Improving readability of the code (though rustfmt is the main tool you should use for this)
- Helping make the code more compact by reducing unnecessary things (my absolute favorite is needless_lifetimes)
- Helping make the code more idiomatic
- Making sure you don’t do things that you’re not supposed to
- Catching mistakes and cases where the code may not work as expected
The last two really are the ones which help with underhanded code. Just to give an example, we have lints like:
- cmp_nan, which disallows things like
x == NaN
- clone_double_ref, which disallows calling
.clone()on double-references (
&&T), since that’s a straightforward copy and you probably meant to do something like
Option<T>is iterable, and while this is useful when composing iterators, directly iterating over an option is usually an indication of a mistake.
- match_same_arms, which checks for identical match arm bodies (strong indication of a typo)
- suspicious_assignment_formatting, which checks for possible typos with the
- unused_io_amount, which ensures that you don’t forget that some I/O APIs may not write all bytes in the span of a single call
These catch many of the gotchas that might crop up in Rust code. In fact, I based my solution of an older, more informal Underhanded Rust contest on one of these.
Clippy is still nightly-only. We hook straight into the compiler’s guts to obtain the information we need, and like most internal compiler APIs, this is completely unstable. This does mean that you usually need a latest or near-latest nightly for clippy to work, and there will be times when it won’t compile while we’re working to update it.
There is a plan to ship clippy as an optional component of rustc releases, which will fix all of these issues (yay!).
But, for now, you can use clippy via:
1 2 3 4 5
If you’re going to be making it part of the development procedures of a crate you maintain, you can also make it an optional dependency.
If you’re on windows, there’s currently a rustup/cargo bug where you may have to add
the rustc libs path in your
cargo clippy to work.
There’s an experimental project called rustfix which can automatically apply suggestions from clippy and rustc to your code. This may help in clippy-izing a large codebase, but it may also eat your code and/or laundry, so beware.
There’s a lot of work that can be done on clippy. A hundred and eighty lints is just a start, there are hundreds more lint ideas filed on the issue tracker. We’re willing to mentor anyone who wants to get involved; and have specially tagged “easy” issues for folks new to compiler internals. In general, contributing to clippy is a great way to gain an understanding of compiler internals if you want to contribute to the compiler itself.
If you don’t want to write code for clippy, you can also run it on random crates, open pull requests with fixes, and file bugs on clippy for any false positives that appear.
There are more tips about contributing in our CONTRIBUTING.md.
I hope this helps reduce mistakes and underhandedness in your code!
..unless you’re writing code for the Underhanded Rust competition. In that case, underhand away!