Files
doos/valgrind/README.md
T

63 lines
3.7 KiB
Markdown

# 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/<step>.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).