Files
tailscale/tstest/integration
Brad Fitzpatrick efc17c4e50 tstest/integration: fix ETXTBSY race when copying test binaries
Tests that exec a fresh CopyTo copy of the tailscale/tailscaled
binaries occasionally failed with "text file busy" on GitHub
Actions, previously worked around with the retry loop in
awaitTailscaledRunnable.

The root cause: CopyTo's Linux hardlink fast path only works while
the built binary is still linked somewhere. The kernel refuses to
hardlink an inode whose link count is zero, even via the still-open
FD, so once the building test's TempDir (and every other test's copy)
has been cleaned up, later tests silently fall through to the
byte-copy path. That path writes the new copy from the test process
itself, and if another parallel test forks a child while the write FD
is open, the child holds the inherited FD (O_CLOEXEC only closes at
exec, not at fork) and a subsequent exec of the fresh copy fails with
ETXTBSY. This is golang.org/issue/22315. On macOS and the BSDs,
CopyTo always takes the byte-copy path, so every copy races there.

Fix it the way the syscall package documents: hold ForkLock for
reading across the copy so that no fork overlaps the lifetime of the
write FD. A standalone stress program reproduced the race in 86 of
200 execs under a fork storm and in 0 of 200 with the lock held.

Updates #15868
Updates #15865

Change-Id: I8a6be72826c9073a9187d7e6c90c8733d2dadb05
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2026-09-14 11:39:40 -07:00
..