Skip to content

context: cap TLS reads during zip import#7017

Open
VrtxOmega wants to merge 1 commit into
docker:masterfrom
VrtxOmega:6917-context-import-tls-size-limit
Open

context: cap TLS reads during zip import#7017
VrtxOmega wants to merge 1 commit into
docker:masterfrom
VrtxOmega:6917-context-import-tls-size-limit

Conversation

@VrtxOmega

Copy link
Copy Markdown

- What I did

Applied the existing context-import decompressed read limit to TLS entries in zip context archives.

Fixes #6917

- How I did it

  • wrapped zip tls/ entry reads with limitedReader, matching meta.json
  • tightened limitedReader so it probes the underlying reader at the zero-remaining boundary and reports data beyond the limit
  • added regressions for oversized compressed TLS entries and the exact limit-plus-one reader boundary

- How to verify it

GO111MODULE=auto GOPATH=<temp-gopath> go test github.com/docker/cli/cli/context/store -run 'TestImportZipRejectsOversizedTLSFile|TestLimitReaderReadAll' -count=1
GO111MODULE=auto GOPATH=<temp-gopath> go test github.com/docker/cli/cli/context/store -count=1
GO111MODULE=auto GOPATH=<temp-gopath> go test github.com/docker/cli/cli/context/store github.com/docker/cli/cli/command/context -count=1
git diff --check

- Human readable description for the release notes

`docker context import` now enforces the decompressed size limit for TLS material in zip archives.

Signed-off-by: Rage Lopez <VrtxOmega@pm.me>
Comment on lines -19 to -21
if l.N == 0 {
return 0, io.EOF
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Curious; why was this condition removed?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good question — this was intentional to fix a boundary detection gap with chunked reads.

The old early-return at l.N == 0 silently masks data beyond the limit as io.EOF when the underlying reader delivers data in chunks. This is realistic for zip decompression streams that don't deliver all bytes in a single Read() call.

The bug scenario:

// Underlying reader has 5 bytes but delivers them in 2-byte chunks: AB, CD, E
// Limit is 4 bytes
r := &chunkedReader{data: []byte("ABCDE"), chunkSize: 2}
lr := &limitedReader{R: r, N: 4}
data, err := io.ReadAll(lr)

// OLD: reads AB (N=2), reads CD (N=0), next call returns io.EOF
//      → ReadAll returns ("ABCD", nil) — SILENT TRUNCATION!
//      The 5th byte "E" is never detected.

// NEW: reads AB (N=2), reads CD (N=0), next call probes with 1-byte read,
//      gets "E" (N=-1) → returns error
//      → ReadAll returns ("ABCD", "read exceeds the defined limit") — CAUGHT!

The old code's if l.N == 0 { return 0, io.EOF } tells io.ReadAll "stream ended cleanly" when the underlying reader actually has more data beyond the limit. This is a security concern for the zip bomb fix — if a TLS entry decompresses to exactly 10MB in chunks, the old code returns EOF. If it's 10MB+1 byte delivered as (10MB chunk) + (1 byte chunk), the old code also returns EOF after reading 10MB, silently truncating without error.

The new code removes that early return and probes the underlying reader at the zero-remaining boundary: when N reaches 0, the next read is capped to 1 byte (N+1). If the underlying reader returns data, N goes to -1 and we report the violation. If it returns io.EOF, we propagate it cleanly.

The regression test validates this — the "Tes" (3 bytes, N=2) case catches both implementations via the N+1 cap, but the chunked scenario is where the old code fails.

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@VrtxOmega

Copy link
Copy Markdown
Author

Two CI checks are failing on the metadata side — both require maintainer action:

check-changelog — The PR body has a markdown changelog block, but the PR needs an impact/changelog label for the check to pass. Could a maintainer add that label?

validate-milestone — The PR needs a milestone set to match VERSION (currently 29.6.0). Could a maintainer assign the 29.6.0 milestone?

All 90 test/lint/build checks pass — just these two gating metadata checks remain.

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.

docker context import TLS Entry Handling results in OOM

3 participants