# valgrind **Scope:** [README](../README.md) · [AGENTS](../AGENTS.md) · [docs/RESEARCH](../docs/RESEARCH.md) Memcheck run against a fixed, named set of `doos` invocations, to catch real memory bugs (invalid reads/writes, use-after-free, definite leaks) in doos's own code: namespace setup, mount handling, the CLI, the image store. Separate from `audit/` (named CVEs against other software) and from `tests/run.sh` (behavior, not memory safety). ## Running it ``` make valgrind # builds build/doos first, then runs scripts/valgrind.sh against it scripts/valgrind.sh PATH # run directly against a specific binary, e.g. a copy with an # installed AppArmor profile (see README's apparmor section) ``` Reports land in `build/valgrind-reports/.log`, one per invocation, kept (not deleted) after the run so a failure can be inspected. Each step prints `ok` or `FAIL`; the run exits non-zero only if valgrind itself flagged something, never because `doos` exited non-zero for its own reasons (an expected `delete` dry-run notice, an AppArmor block on a binary with no profile installed, and so on, all exit non-zero as designed and are not memory bugs). ## What it checks A fixed, capped list (`scripts/valgrind.sh`): `list`, `get_or_create`, `attach` (twice, to exercise both the create path and joining an already-running container), `image list`, `delete --yes`. All run with `--net none` and no image, so the check needs no network and no download; it is about doos's own allocation code, not about what happens after it execs the contained command. ## Suppressions (`doos.supp`) Six specific, named, commented allocation sites, each captured with `valgrind --gen-suppressions=all` against a real run and explained in the file itself: the clone(2) child's 1 MiB stack buffer (lifetime tied to the child process, not a scope), and five "an error path constructed an exception or opened a stream, then the namespace child exited right after" patterns (`doMount`'s thrown `SysError`'s message string and the exception object itself, once for a mount failure inside `setupRoot` and once for the mount that runs directly in `containerInit` before it, such as an AppArmor-blocked `mount bind /`; plus an `ifstream` opened in `containerInit`'s cold path). None of these are "definitely lost"; valgrind calls all of them "possibly lost" because the process ends before the normal C++ unwind would run their destructors, which is expected for a namespace child that is about to disappear regardless. A genuinely new leak anywhere else is not covered by these exact stacks and still fails the check. To add a suppression for a new, understood, one-time allocation: reproduce it, add `--gen-suppressions=all` to the `valgrind` invocation in `scripts/valgrind.sh` temporarily, copy the exact block `doos.supp` already prints into the file with a name and a comment explaining why it is not a real leak, then remove the temporary flag. Never add a suppression you cannot explain in one sentence. ## What this does not cover - Anything that happens after doos execs the user's command: valgrind traces doos's own process tree, not an arbitrary program run inside the container. - Image download and extraction (`--image`): not in the fixed list, since it needs network and adds `curl`/`tar`/`gpg` subprocess noise that is not doos's own memory. Add a step to `scripts/valgrind.sh` following the same pattern if that code path needs a dedicated run. - `slirp4netns`/`pasta`: separate processes, not doos; see `audit/CVE-2020-7039` for how their memory safety is tracked instead (upstream version, not valgrind). Back to [README](../README.md), [AGENTS](../AGENTS.md), [docs/RESEARCH](../docs/RESEARCH.md).