Skip to content

test: bind the fake resolv.conf at the RESOLVED path, not at a symlink - #587

Merged
vyskocilm merged 1 commit into
mainfrom
test/resolvconf-symlink-dest
Sep 18, 2026
Merged

vyskocilm merged 1 commit into
mainfrom
test/resolvconf-symlink-dest

Conversation

@vyskocilm

Copy link
Copy Markdown
Contributor

CI red on main and every PR since 2026-09-17 19:21 UTC. Failure names neither DNS nor bwrap:

panic: integration: TestNoResolverHostFailsFastAndSaysSo has run for more than
twice its 10s time budget and is presumed hung.

Runs 35264394910 (main, f85489d), 35265056261, 35266080160 (#585). Three regions, one image, same test.

Cause is not ours

No snug change between last green main (1487aec, 14:41) and first red (f85489d, 19:21) touches integration code — that commit is a comment, two unit test files and a design doc. Delta is the RUNNER:

run bubblewrap
35235235842 green 0.9.0-1ubuntu0.1
35264394910 red 0.9.0-1ubuntu0.2

0.9.0-1ubuntu0.2 is CVE-2026-87766. Second patch: "creates and resolves destinations without following symlinks, and rejects mount operations on symlink destinations".

/etc/resolv.conf on a systemd-resolved host — every GitHub runner — is a symlink to ../run/systemd/resolve/stub-resolv.conf. Measured under an outer bwrap holding such an /etc (bwrap 0.12.0 locally carries the same refusal):

$ bwrap --dev-bind / / --ro-bind $T /etc/resolv.conf -- true
bwrap: Can't mount on symlink destination /etc/resolv.conf
rc=1

singleFileResolvConfBind() answered false, every call took the fallback shape — copy the whole of /etc, ~19s each on a runner — and four such calls in one test blew a 10s budget() whose watchdog panics without flushing the test's log. Hence a hang naming nothing.

Fix

resolvConfDest() = EvalSymlinks("/etc/resolv.conf"), cached; probe and bind both use it. Fixture lands where the symlink points, hostNameservers()'s plain os.ReadFile follows the link and sees it. Literal path when the link dangles, probe answers for that.

Three things beside it:

  • Fallback ANNOUNCES itself on stderr carrying bwrap's own message. ~19s per call against a 10s budget is the shape that costs the job, and a watchdog panic cannot print a t.Logf.
  • fakeHostEtc caches per content under TestMain's dir (new integrationTmp): fallback costs one copy per process, not one per call.
  • TestTheFakeResolvConfHarnessReallySubstitutes — the file's only POSITIVE use of the overlay. Fixture names 198.51.100.53 (TEST-NET-2); it must come back out of the sandbox's generated resolv.conf and off --dry-run's screen. Every other assertion here is a negative, and a negative passes just as well when the overlay silently does not apply.

No production code touched, no golden argv diff, no profile/mount/seccomp/host-integration surface.

Verification

  • make gate green.
  • SNUG_REQUIRE_SANDBOX=1 make integration-sandbox green, 310.223s, engine tests: 46 ran, floor 46 — podman version 6.0.2.
  • Three DNS tests green under a wrapper reproducing the runner shape (an /etc whose resolv.conf is a symlink), where the probe fails with the message above. Dangling-symlink arm exercised too: warning prints, fallback still passes.

🤖 Generated with Claude Code

CI went red on main and every PR from 2026-09-17 19:21 UTC with

  panic: integration: TestNoResolverHostFailsFastAndSaysSo has run for more
  than twice its 10s time budget and is presumed hung

naming neither DNS nor bwrap. No snug change between the last green run
(1487aec, 14:41) and the first red one (f85489d, 19:21) touches integration
code: the delta is the runner's bubblewrap, 0.9.0-1ubuntu0.1 -> 0.9.0-1ubuntu0.2,
the CVE-2026-87766 fix, whose second patch "creates and resolves destinations
without following symlinks, and rejects mount operations on symlink
destinations".

/etc/resolv.conf on a systemd-resolved host — every GitHub runner — is a
symlink to ../run/systemd/resolve/stub-resolv.conf. Measured under an outer
bwrap holding such an /etc:

  $ bwrap --dev-bind / / --ro-bind $T /etc/resolv.conf -- true
  bwrap: Can't mount on symlink destination /etc/resolv.conf
  rc=1

So singleFileResolvConfBind() answered false, every call took the fallback
shape (copy the whole of /etc, ~19s each on a runner), and the four calls in
TestNoResolverHostFailsFastAndSaysSo blew a 10s budget whose watchdog panics
without flushing the test log.

Fix: resolvConfDest() = EvalSymlinks("/etc/resolv.conf"), and both the probe
and the bind use it. The fixture lands where the symlink points, so
hostNameservers()'s plain os.ReadFile still sees it. Literal path when the
link dangles, and the probe then answers for that.

Three things beside it:

- The fallback ANNOUNCES itself on stderr with bwrap's own message. ~19s per
  call against a 10s budget is the shape that costs the job, and budget()'s
  panic cannot print a t.Logf.
- fakeHostEtc caches per content under TestMain's directory (new
  integrationTmp), so the fallback costs one copy per process, not one per
  call.
- TestTheFakeResolvConfHarnessReallySubstitutes is the file's only POSITIVE
  use of the overlay: a fixture naming 198.51.100.53 (TEST-NET-2) must come
  back out of the sandbox's generated resolv.conf and off --dry-run's screen.
  Every other assertion here is a negative, and a negative passes just as
  well when the overlay silently does not apply.

Verified: make gate green; SNUG_REQUIRE_SANDBOX=1 make integration-sandbox
green (310.223s, engine tests 46 ran, floor 46, podman 6.0.2); and the three
DNS tests green under a wrapper reproducing the runner's shape — an /etc whose
resolv.conf is a symlink — where the probe previously failed with the message
above. Local bwrap is 0.12.0, which carries the same refusal.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@vyskocilm
vyskocilm merged commit bdf1ce0 into main Sep 18, 2026
5 checks passed
@vyskocilm
vyskocilm deleted the test/resolvconf-symlink-dest branch September 18, 2026 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant