Repository navigation
on-prem locks: hold on Ubuntu 26.04 (uutils mkdir) and never break a live writer's lock - #1922
Conversation
A writer that found the audit lock owned by an exited process removed it without re-checking. If the owner released between the read and the removal and another writer took the lock, the breaker deleted the live lock and two writers could chain to the same parent. Break stale locks only under a second lock and only if the lock still names the same dead owner. Add a deterministic test that forces the handover, and print the verifier output when the concurrent-writer check fails.
…r(1) Ubuntu 26.04 ships uutils mkdir, which reports success to both callers when two processes race to create one directory. The audit-log writer lock and the release update lock both treated a zero exit from mkdir as ownership, so on that image concurrent audit writers chained to the same parent (the "concurrent audit writers preserve one hash chain" CI failure) and a second update could proceed beside a live one. Ownership is now the shell's own noclobber create of the pid file inside the lock directory (O_CREAT|O_EXCL), and the stale-lock breaker lock is an exclusive file too. Tests model the uutils behavior with a mkdir shim that never reports EEXIST; on the previous code they fail.
Independent adversarial review: BLOCKP2: Refuse an existing nonregular pid path before granting ownership. The noclobber writes in audit-log.sh:83 and lib-release.sh:352 succeed when The later Validation on WSL Bash 5.2.21: the exact advertised shell suite passed 51/51. The frozen 13-case probe produced 2 failures/11 passes on head |
|
Answering BLOCK 6648159753 at a932d9d. P2, an existing nonregular pid path: both lock helpers ( New checks in test-update.sh: a FIFO with a reader pre-created at the pid path. The update is refused and the FIFO is left in place. The audit writer exits nonzero, appends nothing, and leaves the FIFO in place. Against 6798c44 the five new checks fail. The update also goes through there, which fails 2 later rollback checks (47 passed, 7 failed). With the fix: macOS bash 54/54 three times, and Linux (GNU bash 5.2.37, coreutils 9.7) 56/56 three times. The mkdir-shim and 12-writer chain checks still pass in each run. shellcheck shows only the existing SC1091 info. Not tested: an actual Ubuntu 26.04/uutils host, and uutils |
Independent adversarial review: APPROVERe-reviewed Fresh gated Linux validation: all 13 original independent cases and four additional FIFO-refusal cases passed, with zero errors or skips. The four update/audit cases, with and without a FIFO reader, finish nonzero within the frozen three-second bound, preserve the FIFO inode/type and append nothing. The exact parent’s retained 2-failure/11-pass result is reused, not rerun: two live update callers entered, and two audit writers appended a broken chain under the date scheduling shim. On this head those original cases admit no owner or audit append. Live-owner exclusion, dead-owner recovery, handoff controls and five native rounds of 12 audit writers also pass, preserving all 60 unique jobs and their chains. The unchanged author shell suite passes 56 checks, including its two GPG checks. Bash syntax and release consistency pass; shellcheck was unavailable. Fifteen head-source and six frozen-input hashes match before and after; both detached worktrees remain clean. I read the actual lint, source-boundary and critical logs: configured/strict mypy pass, source tests pass 73, and critical suites pass 52 plus 183 with named golden VERIFIED and zero-model positives. All five associated workflows succeeded at synthetic merge Limits: native binaries were GNU coreutils 9.4 and Bash 5.2.21; the mkdir/cat/date shims are fault or scheduling models, with no native Ubuntu 26.04/uutils race reproduction. Fixtures pre-create owned FIFO paths. The author’s signed-release fixture uses placeholder wheels, a build hook and disposable keys, so it does not validate an actual package installation. No fresh parent/main or full Flow rerun, real deployment, privilege-escalation or egress-isolation claim. |
Found while testing #1918 (on-prem offline install).
What this fixes
The on-prem audit log is a hash chain. audit-log.sh serializes writers with a lock, and the release updater (lib-release.sh) uses a similar lock so that two updates or rollbacks can't run at once. Both locks treated a zero exit status from
mkdir LOCKDIRas ownership.That isn't safe on Ubuntu 26.04, which is the image GitHub's runner used for #1918. Its /usr/bin/mkdir is uutils (the Rust coreutils). In the version it ships, when two processes race to create the same directory, the loser's EEXIST is swallowed and both exit 0. A plain
mkdiron a directory that already exists still fails, so this only shows up under contention. Others have reported the same behavior (gaia-harness/gaia#3084 measured double success in 236 of 300 two-way races on ubuntu:26.04 with GNU mkdir at 0 of 300; cahenesy/throughline#148). As a result:The change
mkdir -pof the lock directory is no longer what grants the lock. The on-disk layout (LOCK/pid) is unchanged, so an existing holder is still recognized.Tests (test-update.sh)
Testing
Not tested