Skip to content

vendor: github.com/moby/go-archive v0.3.2 - #53261

Merged
vvoland merged 1 commit into
moby:masterfrom
thaJeztah:bump_go_archive_0.3.2
Jul 31, 2026
Merged

vendor: github.com/moby/go-archive v0.3.2#53261
vvoland merged 1 commit into
moby:masterfrom
thaJeztah:bump_go_archive_0.3.2

Conversation

@thaJeztah

@thaJeztah thaJeztah commented Jul 31, 2026

Copy link
Copy Markdown
Member

Summary

Release notes (optional)

Fix a regression where the `CopyToContainer` rejects container paths that traverse absolute symlinks, such as `/var/run` -> `/run`.

A picture of a cute animal (not mandatory but encouraged)

@thaJeztah thaJeztah added this to the 29.8.0 milestone Jul 31, 2026
Copilot AI review requested due to automatic review settings July 31, 2026 14:36
@thaJeztah thaJeztah changed the title Bump go archive 0.3.2 vendor: github.com/moby/go-archive v0.3.2 Jul 31, 2026
@thaJeztah

Copy link
Copy Markdown
Member Author

Verified that the reproducer from passes; #53258 (comment)

./repro.sh
Narrow archive contents:
.git/
.git/HEAD
CONTROL PASS: narrow-root request returned HTTP 200
CONTROL PASS: narrow-root variant created the expected file

Broad archive contents:
var/run/act/actions/broad/.git/
var/run/act/actions/broad/.git/HEAD
PASS: broad-root request returned HTTP 200
PASS: broad-root variant created the expected file

Result: not affected

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 8 changed files in this pull request and generated no new comments.

Suppressed comments (2)

go.mod:14

  • go.mod introduces a temporary replace to a personal fork (github.com/thaJeztah/go-archive) with a "FIXME" note. This makes the dependency provenance and long-term reproducibility unclear for a vendored dependency; for an upstream merge it should be updated to a released/official github.com/moby/go-archive version (or otherwise clearly justified) and the temporary replace removed before landing.
// FIXME(thaJeztah): testing https://github.com/moby/go-archive/pull/93
replace github.com/moby/go-archive => github.com/thaJeztah/go-archive v0.0.0-20260731141554-6c3be27c2c56

go.mod:71

  • The PR title says go-archive v0.3.2, but go.mod currently requires github.com/moby/go-archive v0.3.1 (and vendor/modules.txt also reflects v0.3.1). Please align the declared version with the PR intent (either bump to the intended version or update the PR metadata accordingly) so it’s clear what’s being shipped.
	github.com/moby/docker-image-spec v1.3.1
	github.com/moby/go-archive v0.3.1
	github.com/moby/ipvs v1.1.0

@GordonTheTurtle GordonTheTurtle left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessment: 🔴 CRITICAL

This PR is intentionally a WIP / pre-release testing PR (stacked on #53260, awaiting the upstream moby/go-archive#93 release). The vendor changes themselves look correct and security-conscious — the new resolveArchivePath / resolveHardlinkTarget logic is a sound improvement. However two items must be resolved before merge.


Inline findings (2):

# Severity Finding
1 🔴 High FIXME replace directive routes moby/go-archive to a personal fork
2 🟡 Medium require pins v0.3.1 but PR title advertises v0.3.2

Dismissed (1):

# Reason
go.sum missing moby/go-archive v0.3.1 hash Expected/correct: Go only checksums the replacement module when a replace directive is active

Lower-confidence observations (informational, not blocking):

  • vendor/modules.txt: the => replace annotation is a go mod vendor artifact; will self-correct when the replace directive is removed
  • resolveArchivePath has a documented TOCTOU window between Stat and the subsequent Root operation — bounded by os.Root containment (no escape possible), and explicitly acknowledged in the comment; the trade-off is acceptable
  • createImpliedDirectories: the refactor drops the hdr.Typeflag != tar.TypeDir guard, making the function also run for TypeDir entries (idempotent, harmless, but undocumented)

Merge blocker: remove the replace directive and align module version once go-archive#93 ships.

Comment thread go.mod Outdated
Comment thread go.mod Outdated
@thaJeztah

Copy link
Copy Markdown
Member Author

Windows failure is unrelated;

=== FAIL: integration/container TestHealthCheckProcessKilled (3.92s)
    health_test.go:111: polling check failed: expected "Health check exceeded timeout (50ms): logs1 logs2 logs3\n", got "Health check exceeded timeout (50ms)"

full diff: moby/go-archive@v0.3.1...v0.3.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Copilot AI review requested due to automatic review settings July 31, 2026 16:51
@thaJeztah
thaJeztah force-pushed the bump_go_archive_0.3.2 branch from 7caf41c to eeea07c Compare July 31, 2026 16:51
@thaJeztah
thaJeztah marked this pull request as ready for review July 31, 2026 16:51
@thaJeztah thaJeztah modified the milestones: 29.8.0, 29.7.1 Jul 31, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 8 changed files in this pull request and generated no new comments.

@vvoland
vvoland merged commit c5b8ce9 into moby:master Jul 31, 2026
64 of 69 checks passed
@thaJeztah
thaJeztah deleted the bump_go_archive_0.3.2 branch July 31, 2026 17:04

@GordonTheTurtle GordonTheTurtle left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessment: 🟡 NEEDS ATTENTION

This PR vendors github.com/moby/go-archive v0.3.2 to fix a CopyToContainer regression where Go 1.25's stricter os.Root incorrectly rejected paths traversing absolute symlinks (e.g. /var/run → /run). The fix is architecturally sound: os.Root containment is preserved throughout, the relativeEscapeBeforeAbsolute guard prevents relative-escape/absolute-link gadget chains, and the unpackedPaths key consistency fix in diff.go is correct.

Two security-adjacent observations worth noting (see inline comments):

  1. Acknowledged TOCTOUresolveArchivePath resolves the path then hands a string to os.Root; a concurrent symlink swap inside the container can redirect writes intra-root, but cannot escape root (os.Root containment holds). Explicitly noted in the code as a known limitation.
  2. isPathEscapes string-sentinel — the security gate relies on matching Go's unexported error string "path escapes from parent". If the string changes in a future Go release the failure mode is a functionality regression (ENOENT returned instead of resolving), not a security bypass — the first switch case already returns the error before the fallback is reached.

Lower-confidence notes

  • relativeEscapeBeforeAbsolute scope (rootpath.go): The flag is only tracked before any absolute link is followed. Post-absolute relative symlinks are not monitored by this flag, but they are still bounded by fsRootPath's chroot-style resolution (the OS-level backstop). Low severity, design-intentional.
  • AUFS metadata ordering (diff.go): Moving createImpliedDirectories after the AUFS-metadata skip is a net correctness improvement; AUFS entries that were immediately continued no longer spuriously trigger parent-directory creation.

))
}

return filepath.Join(relParent, base), nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] Acknowledged TOCTOU race: intra-root path misdirection possible during concurrent container activity

resolveArchivePath calls resolveFSRootPath to follow absolute symlinks (e.g. /var/run → /run), producing a resolved native path string. That string is then handed to an os.Root operation (root.Mkdir, root.OpenFile, root.Link, etc.). A process running concurrently inside the container can swap an absolute symlink's target between these two steps, redirecting the extraction write to a different location within the container root.

The code comment explicitly acknowledges this as a "resolution/use race" and a "compatibility workaround" — the subsequent os.Root call still enforces root containment at the OS level, so no path can escape outside the container root. The worst-case impact is intra-root confusion (e.g. a file lands in /run/foo instead of /var/run/foo after a symlink retarget race), not a container escape.

This is tracked upstream as a known limitation pending handle-relative resolution with resolve-in-root semantics (TODO in the comment). No action required on this PR, but reviewers should be aware of the scope of the fix.

Confidence Score
🟢 strong 100/100

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's a known limitation currently, but does not allow escaping the container rootFs.

return "", err
}

if isPathEscapes(statErr) && (!resolved.followedAbsoluteLink || resolved.relativeEscapeBeforeAbsolute) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LOW] isPathEscapes relies on a hard-coded Go internal error string that could silently degrade on a Go update

The function matches os.Root's path-containment error by unwrapping to the leaf and comparing err.Error() == "path escapes from parent". This is necessary because Go does not yet export errPathEscapes as a public sentinel (tracked at go.dev/issue/74640).

If Go changes this error message or wrapping structure, isPathEscapes silently returns false for path-escape errors. The failure mode is a functionality regression, not a security bypass: the switch in resolveArchivePath evaluates !os.IsNotExist(statErr) && !isPathEscapes(statErr) — if isPathEscapes incorrectly returns false, a genuine path-escape error would be returned immediately at that case branch, preventing the resolveFSRootPath fallback entirely. Legitimate absolute-symlink paths would break again, but no relative-escape path could silently proceed.

Worth adding a test that verifies isPathEscapes correctly identifies os.Root.Stat's error on the current Go toolchain, to catch breakage at CI time rather than at runtime in production.

Confidence Score
🟡 moderate 60/100

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, already upvoted that ticket, but maybe you can find AI friends to also give it a thumbs-up 😉 😂

chhe pushed a commit to chhe/act_runner that referenced this pull request Aug 8, 2026
Revert https://gitea.com/gitea/runner/pulls/1130. Docker 29.7.1 fixed both regressions it worked around, moby/moby#53261 and moby/moby#53260, so only 29.7.0 still needs it.

Verified live with a relative and an absolute `/var/run` symlink: without the workaround the copy passes on 29.4.0, 29.6.2 and 29.7.1, and fails on 29.7.0 alone.

Fixes: https://gitea.com/gitea/runner/issues/1131
Reviewed-on: https://gitea.com/gitea/runner/pulls/1155
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CopyToContainer regression in 29.7.0: "path escapes from parent" when copying into paths that traverse /var/run → /run symlink

4 participants