SmartConfig

Review of M2 chunk D (steps 11-13, the watcher)

Date: 2026-10-01. Full review, as the plan asks for chunk D: three reviewers (AI agents, each in its own clone), on concurrency, on event handling and history, and on security as an unprivileged user against a root watcher. Claude then reproduced each finding before fixing it. Nothing touched the repo, /etc or the real store; attacks ran as uid 1000 through setpriv against a root watcher on temp dirs.

Findings and fixes

Lens Finding Severity Fixed in
security A user could make the root watcher store a root-only file: a directory named authorized_keys in ~/.ssh was recorded as a whole tree, and a directory in it swapped for a symlink was followed. Reproduced: a 0600 root file’s content stored. high 1beb8f3: nothing below .ssh entries is recorded, and the watcher reads through ReadStateBelow (O_NOFOLLOW at every level from the root). Each layer has its own test.
events A path whose directory became a file looped as “moved” forever (ENOTDIR), which also stopped the baseline summary. high 962a118
concurrency A moved directory over MaxDirty crashed the worker with a nil pointer; with mu held, Run could hang instead of exiting. high 962a118 (crash), 3cbbd2d (Run returns within 5 s after any panic)
concurrency A reader’s walk racing a rescan left new directories unwatched (1 in 10-20 runs). high 962a118: watches are tagged with the current generation; 0 of 20 runs now.
events A file under a directory that became a symlink, and a file replaced by a directory, FIFO or socket, never got a deleted row. medium 962a118
events, concurrency A directory moved away with a file put at its name at once: the file waited for the hourly rescan. medium 962a118
concurrency Rescan marks counted against MaxDirty, so a busy host rescanned in a loop and lost work (80 of 300 changes in 3 s). medium 3cbbd2d: 300 of 300, 1 rescan.
concurrency Stopping during a rescan logged one error per directory; hashing under mu at the disk floor. low 3cbbd2d
security scd.lock without O_NOFOLLOW; logOnce memory unbounded; user-file limits skipped root-owned files under a home. low 1beb8f3
events visudo in sudoers.d kept foo.tmp recorded at tier 2 (sudo skips dotted names). low 962a118 (scope)
tests The barrier helper missed moved directories; overflow tests could hang on failure; the rate-limit test could skip; several tests could not fail. gaps 962a118, 3cbbd2d

Not changed, with reasons