Rust: unsafe, FFI and security review
You learn the limits of unsafe code, interoperating with C and the real security bugs of Rust projects, and verify your code with miri and fuzzing.
Level 501 · 17 lessons · 18 practice labs
Lessons
unsafe
- What unsafe unlocks and what it does not
- Raw pointers and undefined behaviour
- Building a safe abstraction on top of unsafe
- Catching undefined behaviour with miri
- Short task: find and fix a faulty unsafe block
Working with C
- Calling a C library from Rust
- Exposing Rust to C as a library
- bindgen and cbindgen
- Memory ownership across the boundary
- Short task: wrap a C parser in a safe Rust interface
Security review
- Bugs Rust still has: panics, overflow, logic
- Integer overflow in release builds
- Denial of service through panics and excessive allocation
- Fuzzing with cargo-fuzz
- The crate supply chain: build.rs and proc-macro risks
- Short task: fuzz a parser and fix it
Capstone
- Capstone: port a parser written in C to safe Rust
Practice labs
- Writing justifications for unsafe blocks: Add SAFETY comments and debug_assert checks to three unsafe blocks in a small module and replace the one with no justification by safe code.
- Swapping values with raw pointers: Write an unsafe function that swaps two values through raw pointers and document its conditions in a SAFETY comment.
- Catching undefined behaviour with miri: Inspect unsafe slice code that passes its tests but fails under miri, find the cause and fix it.
- A safe ring buffer on top of unsafe: Complete the safe interface of a ring buffer that uses MaybeUninit so miri and the bounds tests pass cleanly.
- Repairing code that breaks aliasing rules: Find the unsafe helper that creates two &mut to the same data, make it safe by changing its API and prove it with miri.
- Calling a C function from Rust: Call the checksum function of a small C library through an extern declaration and compare it with a Rust implementation.
- Ownership of memory crossing the boundary: Fix code that leaks or double-frees CString memory handed to C so valgrind reports clean.
- Exposing Rust code as a C library: Export a validation function with extern "C", generate a header with cbindgen and show the C test program runs without seeing a panic.
- Catching silent overflow in release builds: Fix a size calculation that is wrong only in release builds with checked_mul and test it under both profiles.
- Closing inputs that cause panics: Replace unwraps and direct indexing in a packet parser with checks that return Result.
- Limiting excessive allocation: Put an upper bound on a decoder that trusts a length field for Vec::with_capacity and pass the test with huge lengths.
- An authorisation bug the compiler cannot catch: Find the inverted condition in Bulutsu Yazılım's role check by review, fix it and add a regression test.
- Trying a TLV decoder with cargo-fuzz: Write a cargo-fuzz target for a ready TLV decoder and run it until it finds the first crash.
- Closing a fuzzer crash for good: Minimise the crash input the fuzzer left, fix the root cause and turn the input into a permanent regression test.
- Reviewing a suspicious build.rs: Spot code in a dependency's build.rs that reaches the network and reads environment variables, report it and pin a safe version.
- Fixing a wrong unsafe impl Sync: Show with miri that a counter type hand-marked as Sync causes a data race across threads, then remove the declaration and rebuild the type with safe synchronisation tools.
- Auditing unwrap calls on untrusted input: List the unwrap and expect calls on a small service's input path, change the ones reachable from outside to return errors and write a short justification for the rest.
- Comparing two parsers with structure-aware fuzzing: Write a fuzz target that feeds command structures generated with Arbitrary to both the new and the reference parser and stops when the results diverge, then fix the difference found and add the example to the tests.
About this course
You learn the limits of unsafe code, interoperating with C and the real security bugs of Rust projects, and verify your code with miri and fuzzing.
What you will learn
- unsafe
- Working with C
- Security review
- Capstone
Prerequisites
You should be experienced in the topics of the same path up to level 401.
Lab environment
Lessons run in your own Kali Linux terminal that opens in the browser. Nothing to install; the lab starts clean in every lesson.
Frequently asked questions
- Is this course right for my level?
- The course is at level 501; it assumes you know the topics of the lower-level courses.
- Is there anything to install?
- No. Every lesson runs in your own lab environment that opens in the browser.
- How do lessons work?
- Each lesson has 8 to 10 steps and a short quiz. Early steps show the command to type, later steps give less help; if two attempts fail, the solution opens.
- Can I try it for free?
- Yes. The first three lessons of every course are free.