Exercises for array bounds
String manipulation code very often runs into array bounds.
For simple exercises, arrange two-part problems where the first part allocates memory for a string and the second part modifies that string in place. This forces students to distinguish between the data size and the allocated size. For example: read a line from a file by growing a buffer until you find a newline; then perform some transformation that might expand the line (e.g. a substring replacement or a format conversion).
For some more complex exercises, do some parsing of binary protocols with length-value encodings. This is a very common source of buffer overflows in real-world code. For example, one that allowed exploiting computers provided that they were powered off! (A bug in a Wake-on-LAN implementation in network card firmware.) This forces students to distinguish between the purported size (encoded in the string) and the actual size (bounded by the size of the complete input).
Exercises for memory management
In high-quality C code bases, buffer overflows tend to be rare, because it's not that hard to be disciplined about them. Memory management bugs are harder. Manual memory management is hard, and often the reason you need it is that you can't be very disciplined about it. So you should train your students to watch out for use-after-free and double-free, as well as memory leaks.
A very common case for a memory management bug is a program that needs to allocate two resources (such as two malloc'ed objects), and where a recoverable error can happen either after obtaining the first resource or after obtaining the second resource.
- If the cleanup code tries to access the a resource whose allocation failed, you may have an access to uninitialized memory (possibly leading to dereferencing an arbitrary pointer object). In more disciplined code, where all pointers are initialized to
NULL, you have a null pointer dereference.
- If the cleanup code tries to access one of the resources after it has deallocated it, you may have a use-after-free.
- If the cleanup code ends up freeing a resource twice (e.g. due to partial cleanup on a specific error path followed by a generic cleanup), you may have a double free.
- If some cleanup code is missing, you may have a memory leak.
Here are a few examples of long-lived programs that allocate multiple resources for a single task:
- A server allocates a send buffer and a receive buffer.
- A data processing server, or a data processing interactive tool, that allocates multiple buffers to process each request.
Memory management errors also arise in code that manipulates complex data structures (trees, linked lists, hash tables, graphs, …). But that tends to make for more advanced exercises because the parts about the data structure itself are usually complex.
Please teach your students tooling that can help them. As a practitioner, I find ASan (Address Sanitizer) very useful. It will find most out-of-bounds accesses, use-after-free/double-free and memory leaks as long as there is a test case that reaches them.