Unit 3.2 - Testing and Fuzzing
Exercise 3.2.1: DNS Decoding
In this exercise you will practice with unit tests, code coverage, and writing a tiny fuzzer using cargo-fuzz
.
You will work with a routine dns_decode
that implements a part of RFC1035 DNS name decoding: in this format, all parts of a domain name are encoded by prefixing them with one byte
indicating the length, followed by the bytes that comprise the part. The domain name itself is zero-terminated (i.e., an "empty part" signifies the end of a domain name).
The periods between parts are not encoded.
So, for example, "mailcrab.tweedegolf.nl" can be encoded as b"\x08mailcrab\x0Atweedegolf\x02nl\0"
. This is subject to two restrictions:
- The maximum size of a part is 63 bytes.
- The maximum size of a full domain name (including periods) is 255 bytes.
We have presupplied a "first attempt" at exercises/3-crate-engineering/2-testing/1-dns-decode
. You will find the function definition in src/lib.rs
and
a src/main.rs
so you can even try the function out interactively. The file src/lib.rs
even contains some unit tests, so we are off to a great start! Although this function is
not terribly well-tested and contains some bugs. Maybe you can find them?
Exercise 3.2.1.A Improving Code Coverage
If you haven't done so already, install cargo-llvm-cov
, by running:
cargo install cargo-llvm-cov
And then the coverage can be viewed by running cargo llvm-cov
from the crate directory. You can also run cargo llvm-cov --open
to inspect the coverage report in your browser.
Even though the line coverage is high, as you can see it is not 100%. Write a unit test so that the coverage for src/lib.rs'
does reach 100% line coverage (you will obviously rarely hit this in practice).
Exercise 3.2.1.B Fuzz Testing
Even with 100% coverage, there are still bugs lurking in here. Let's find them with cargo-fuzz
. To install that, we not only need to install cargo-fuzz
, but also use the "nightly" toolchain of Rust, which contains all the experimental features. To install both, run:
rustup toolchain install nightly
cargo install cargo-fuzz
Then, from exercises/3-crate-engineering/2-testing/1-dns-decode
, run cargo fuzz init
. This will create a subdirectory fuzz/
which contains the template for a fuzzing target.
(Note that this fuzz/
subdirectory is actually a small Rust project, with its own Cargo.toml
file! So you can add dependencies to it as well, as with any other Rust project!)
You can list all the available targets using:
cargo +nightly fuzz list
You can already run the target fuzz_target_1
by running
cargo +nightly fuzz run fuzz_target_1
But that will not do find any bugs; edit fuzz/fuzz_targets/fuzz_target_1.rs
so it does something interesting! Also consider adding more targets using cargo +nightly fuzz add <TARGETNAME
.
Hints:
-
To access the
dns_decode
function, you need to import it in the fuzzing target using:#![allow(unused)] fn main() { use dns_parse::decode_dns_name; }
-
Start simple by writing a fuzzer that just runs
dns_decode
on a&[u8]
input and see if it detects crashes. -
Try checking the two other properties that must hold for
dns_decode
, that is:- the maximum length of a part is 63 bytes
- the maximum length of a domain name is 255 bytes.
-
As in unit tests,
assert!
is your friend.