27 Feb 2017
After the announcement of cargo-fuzz last week, I decided to try using it to search for bugs in capnproto-rust. For a long time I had been meaning to try out afl.rs, a similar fuzz-testing tool about which I had heard lots of good things, but its nontrivial setup costs were enough to deter me. Fortunately, cargo-fuzz is very simple and fast to get running. It quickly found several issues in capnproto-rust, all of which have been fixed in the latest release.
The original
C++ code
suggests that the branches for dealing with Far
pointers in
total_size()
and copy_pointer()
are unreachable,
so I had translated them into panics.
It turns out that these branches can in fact be reached
due to invalid input, rather than any bugs in the code.
In the C++ version, this means that a somewhat
misleading exception will be thrown;
I’ve submitted a pull request
to make the exception more accurate.
The problem is more serious in the Rust version,
because Rust makes a sharp distinction between panics and error Result
s.
One way to attempt to mount a denial-of-service attack on a consumer of Cap’n Proto messages is to carefully craft messages that could trick the consumer into doing lots of work. For example, if you send a cyclic structure, the consumer might go into an infinite loop trying to read it. To protect against such attacks, message readers in capnproto-rust have an adjustable traversal limit, indicating how many bytes are allowed to be read before an error is returned. Reads of zero-sized structs should also count against this limit, as was observed in these bug reports for capnproto-c++ in 2015. I thought that I had completely updated capnproto-rust with fixes for this problem, but apparently I had missed two cases. :-(
The fuzzer managed to find some explicit panics in capnproto-rust that were filling in holes of unimplemented functionality. I had forgotten that such holes still existed. After cargo-fuzz found these, I went ahead and implemented the functionality. At first I was too lazy to write a test case to cover the new code, but before going to bed that night I did set up cargo-fuzz to run on it. By the next morning, cargo-fuzz had found a memory safety issue in the new code! Even better, the test case it generated was rather clever and gave me a good starting point for writing the test that I later added to the capnproto-rust test suite to cover the new functionality.