Phase 0 Projects

Two deliverables. Both small, both sharpen the toolchain reflexes this phase is about. Do them in weeks 2-4 of M1 after Day 7 of the first-week reset. Push both to a Git repo (public or private, doesn’t matter); the point is you can show them and re-read your own writeups in six months.

Project 1: utils — A Multi-file Static Library

Goal: build a small utility library as a Makefile-driven multi-file C project, with a driver program that exercises it, all compiling clean under the strict flag set.

Structure

utils/
├── Makefile
├── include/
│   └── utils.h
├── src/
│   ├── str.c        # string helpers
│   ├── vec.c        # int vector (dynamic array of int)
│   └── file.c       # file I/O helpers
├── tests/
│   └── driver.c     # exercises every function, prints PASS/FAIL
└── README.md

Required Functions

utils.h declares at minimum:

#ifndef UTILS_H
#define UTILS_H
#include <stddef.h>
#include <stdio.h>

/* str.c */
size_t util_strlen(const char *s);
char  *util_strdup(const char *s);          // heap-allocated copy; caller frees
int    util_streq(const char *a, const char *b);

/* vec.c */
typedef struct { int *data; size_t len; size_t cap; } vec_int;
void vec_init(vec_int *v);
void vec_push(vec_int *v, int x);
void vec_free(vec_int *v);

/* file.c */
char *util_read_file(const char *path, size_t *out_len);   // caller frees

#endif

Acceptance Criteria (all must pass)

  1. make clean && make produces build/driver with zero warnings under -std=c17 -Wall -Wextra -Wpedantic -Werror -Wshadow -Wstrict-prototypes -g -O0 -fsanitize=address,undefined.

  2. ./build/driver prints test results and exits 0 on success.

  3. Editing any file in include/ triggers rebuild of the .c files that include it (proves -MMD -MP works).

  4. make release produces a -O2 -DNDEBUG binary with no sanitizers, still zero warnings.

  5. compile_commands.json is generated (via bear -- make) and committed or gitignored deliberately; clangd works on the project in your editor.

  6. README.md documents: what the library does, how to build (debug + release), how to run tests, the flag rationale in one paragraph.

  7. No use of strcpy, strcat, sprintf, or gets. Use snprintf, explicit bounds, strncpy with manual null-termination, or your own helpers.

  8. Every function that allocates memory has a matching free path in the driver, and the driver exits ASan-clean (==0==ERROR never appears).

Stretch (only if you finish early)

  • Add a libutils.a target that produces a real static library archive: ar rcs build/libutils.a $(OBJ), and have the driver link against it (cc build/driver.o -Lbuild -lutils).

  • Add a trivial util_strjoin and util_strsplit so you get a taste of the string type you’ll build in Phase 1.

Time Estimate

8-12 hours across ~1 week. The functions are tiny; the point is the scaffolding (Makefile, headers, tests) and clean sanitizer output.

Project 2: The Segfault Zoo

Goal: reproduce five classic C crashes deliberately, capture the debugger and sanitizer output, and write a paragraph on each explaining what triggered it, what the tools showed, and how you’d prevent it in real code. This teaches you the tool-vs-bug mapping better than any book.

Structure

segfault_zoo/
├── Makefile
├── 01_null_deref/
│   ├── crash.c
│   ├── lldb_output.txt
│   ├── asan_output.txt
│   └── NOTES.md
├── 02_use_after_free/
│   ├── crash.c
│   └── ... (same layout)
├── 03_heap_oob_write/
│   ├── ...
├── 04_double_free/
│   ├── ...
├── 05_stack_overflow/     # infinite recursion
│   ├── ...
└── README.md

The Five Bugs (exact list)

#

Bug

Minimum repro

Signal / detector

1

Null pointer dereference

*(int*)NULL = 42;

SIGSEGV; ASan reports SEGV on unknown address 0x0

2

Use-after-free

free(p); *p = 1;

ASan reports heap-use-after-free with alloc/free stack traces

3

Heap out-of-bounds write

int *p = malloc(4*sizeof(int)); p[10] = 0;

ASan reports heap-buffer-overflow with byte offset

4

Double free

free(p); free(p);

ASan reports attempting double-free

5

Stack overflow via infinite recursion

void f(void){f();} f();

SIGSEGV (stack guard page); lldb bt shows thousands of frames

Acceptance Criteria

  1. Each subdirectory builds with make from the top-level Makefile, into build/01_null_deref, build/02_use_after_free, etc.

  2. Each crash.c compiles clean under -Wall -Wextra -Wpedantic -Werror — the bugs are runtime, not compile-time. This is important: it teaches you that a warning-free build is not a bug-free build.

  3. Each folder has lldb_output.txt capturing a bt and frame variable from an lldb session on the non-sanitizer build.

  4. Each folder has asan_output.txt capturing the ASan report from the sanitized build.

  5. Each folder has NOTES.md (~10 lines): what triggers it, what lldb showed, what ASan showed, how you’d prevent it in real code (e.g., pointer nulling after free, bounds-checked wrappers, assert sentinels).

  6. Top-level README.md summarizes the five bugs and links to each folder.

Time Estimate

6-10 hours. This one is high-signal-per-hour — you learn more per line typed here than almost anywhere else in Phase 0.

What Most People Get Wrong About This Project

They skip the writeup, thinking “I got the crash, I understand it.” You don’t, yet. Forcing yourself to write the five-line NOTES.md — in your own words, six months from now readable — is what converts seeing the crash into understanding it. Every senior C engineer has a mental map of bug type tool that catches it fastest. This project builds yours.

Definition of Done for Phase 0

  • Project 1’s driver exits 0, ASan-clean, with committed README.md.

  • Project 2 has all five subfolders populated with crash, outputs, and NOTES.

  • You can create a new C project from scratch in <5 minutes: directory layout, Makefile from your template, first main.c, bear -- make, clangd working in editor, first commit.

  • You know, from reflex, when to reach for ASan (bad memory), UBSan (bad arithmetic/casts), TSan (races — Phase 4), lldb/gdb (logic bug), or leaks (macOS quick leak check).

When those are true, close Phase 0. Move to Phase 1.


Return to README.md · Next: ../02_c_language_deep/README.md