Description
docker context import handles zip archives where the meta.json branch
correctly limits decompressed reads with limitedReader (10MB cap), but the
tls/ branch uses a bare io.ReadAll(f) with no size limit. Since the raw zip
file is limited to 10MB compressed, but zip compression can achieve 1000:1+
ratios on repetitive data, a crafted zip can contain a TLS entry that
decompresses to gigabytes, crashing the Docker CLI with OOM.
There is inconsistency between two branches in the same function:
// store.go:457-467 — metaFile branch: PROTECTED
if zf.Name == metaFile {
f, err := zf.Open()
// ...
data, err := io.ReadAll(&limitedReader{R: f, N: maxAllowedFileSizeToImport}) // <-- 10MB cap
// ...
// store.go:476-486 — tls/ branch: UNPROTECTED
} else if strings.HasPrefix(zf.Name, "tls/") {
f, err := zf.Open()
// ...
data, err := io.ReadAll(f) // <-- no cap; decompresses to arbitrary size
// ...
}
Reproduce
# zeros compress at ~1000:1 with deflate
zf.writestr('tls/docker/ca.pem', b'\x00' * BOMB_SIZE)
import os
compressed_size = os.path.getsize(OUTPUT)
print(f"Compressed zip size: {compressed_size / 1024:.1f} KB")
print(f"Decompression ratio: {BOMB_SIZE / compressed_size:.0f}:1")
print(f"Output: {OUTPUT}")
print()
print("Run: docker context import bomb-test /tmp/context-bomb.zip")
### Run the POC
```bash
# Generate the bomb
python3 /tmp/generate-bomb.py
# Verify compressed size is well under 10MB
ls -lh /tmp/context-bomb.zip
# Expected: ~500KB-1MB
# Trigger the bug (WARNING: will allocate ~500MB of memory)
docker context import bomb-test /tmp/context-bomb.zip
Expected behavior
There should be checks on size on both branches.
docker version
lient: Docker Engine - Community
Version: 29.3.1
API version: 1.53 (downgraded from 1.54)
Go version: go1.26.1
Git commit: c2be9ccfc3
Built: Wed Mar 25 14:22:32 2026
OS/Arch: darwin/arm64
Context: desktop-linux
Server: Docker Desktop 4.65.0 (221669)
Engine:
Version: 29.2.1
API version: 1.53 (minimum version 1.44)
Go version: go1.25.6
Git commit: 6bc6209
Built: Mon Feb 2 17:16:47 2026
OS/Arch: linux/arm64
Experimental: false
containerd:
Version: v2.2.1
GitCommit: dea7da592f5d1d2b7755e3a161be07f43fad8f75
runc:
Version: 1.3.4
GitCommit: v1.3.4-0-gd6d73eb8
docker-init:
Version: 0.19.0
GitCommit: de40ad0
docker info
docker info
Client: Docker Engine - Community
Version: 29.3.1
Context: desktop-linux
Debug Mode: false
Plugins:
agent: create or run AI agents (Docker Inc.)
Version: v1.29.0
Path: /Users/daniel/.docker/cli-plugins/docker-agent
ai: Docker AI Agent - Ask Gordon (Docker Inc.)
Version: v1.19.0
Path: /Users/daniel/.docker/cli-plugins/docker-ai
buildx: Docker Buildx (Docker Inc.)
Version: v0.32.1-desktop.1
Path: /Users/daniel/.docker/cli-plugins/docker-buildx
compose: Docker Compose (Docker Inc.)
Version: v5.1.0
Path: /Users/daniel/.docker/cli-plugins/docker-compose
debug: Get a shell into any image or container (Docker Inc.)
Version: 0.0.47
Path: /Users/daniel/.docker/cli-plugins/docker-debug
desktop: Docker Desktop commands (Docker Inc.)
Version: v0.3.0
Path: /Users/daniel/.docker/cli-plugins/docker-desktop
dhi: CLI for managing Docker Hardened Images (Docker Inc.)
Version: v0.0.0-alpha
Path: /Users/daniel/.docker/cli-plugins/docker-dhi
extension: Manages Docker extensions (Docker Inc.)
Version: v0.2.31
Path: /Users/daniel/.docker/cli-plugins/docker-extension
init: Creates Docker-related starter files for your project (Docker Inc.)
Version: v1.4.0
Path: /Users/daniel/.docker/cli-plugins/docker-init
mcp: Docker MCP Plugin (Docker Inc.)
Version: v0.40.1
Path: /Users/daniel/.docker/cli-plugins/docker-mcp
model: Docker Model Runner (Docker Inc.)
Version: v1.1.5
Path: /Users/daniel/.docker/cli-plugins/docker-model
offload: Docker Offload (Docker Inc.)
Version: v0.5.70
Path: /Users/daniel/.docker/cli-plugins/docker-offload
pass: Docker Pass Secrets Manager Plugin (beta) (Docker Inc.)
Version: v0.0.24
Path: /Users/daniel/.docker/cli-plugins/docker-pass
sandbox: Docker Sandbox (Docker Inc.)
Version: v0.12.0
Path: /Users/daniel/.docker/cli-plugins/docker-sandbox
sbom: View the packaged-based Software Bill Of Materials (SBOM) for an image (Anchore Inc.)
Version: 0.6.0
Path: /Users/daniel/.docker/cli-plugins/docker-sbom
scout: Docker Scout (Docker Inc.)
Version: v1.20.1
Path: /Users/daniel/.docker/cli-plugins/docker-scout
Server:
Containers: 8
Running: 2
Paused: 0
Stopped: 6
Images: 3
Server Version: 29.2.1
Storage Driver: overlayfs
driver-type: io.containerd.snapshotter.v1
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 2
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
CDI spec directories:
/etc/cdi
/var/run/cdi
Discovered Devices:
cdi: docker.com/gpu=webgpu
Swarm: active
NodeID: ofx411xzsjxecd11xami0kk4d
Is Manager: true
ClusterID: q326fzbjro7hy14dsad1u9k7q
Managers: 1
Nodes: 1
Data Path Port: 4789
Orchestration:
Task History Retention Limit: 5
Raft:
Snapshot Interval: 10000
Number of Old Snapshots to Retain: 0
Heartbeat Tick: 1
Election Tick: 10
Dispatcher:
Heartbeat Period: 5 seconds
CA Configuration:
Expiry Duration: 3 months
Force Rotate: 0
Autolock Managers: false
Root Rotation In Progress: false
Node Address: 192.168.65.3
Manager Addresses:
192.168.65.3:2377
Runtimes: io.containerd.runc.v2 runc
Default Runtime: runc
Init Binary: docker-init
containerd version: dea7da592f5d1d2b7755e3a161be07f43fad8f75
runc version: v1.3.4-0-gd6d73eb8
init version: de40ad0
Security Options:
seccomp
Profile: builtin
cgroupns
Kernel Version: 6.12.76-linuxkit
Operating System: Docker Desktop
OSType: linux
Architecture: aarch64
CPUs: 8
Total Memory: 7.653GiB
Name: docker-desktop
ID: 4413ecca-8450-43ca-bca2-80d61e5f6784
Docker Root Dir: /var/lib/docker
Debug Mode: false
HTTP Proxy: http.docker.internal:3128
HTTPS Proxy: http.docker.internal:3128
No Proxy: hubproxy.docker.internal
Labels:
com.docker.desktop.address=unix:///Users/daniel/Library/Containers/com.docker.docker/Data/docker-cli.sock
Experimental: false
Insecure Registries:
hubproxy.docker.internal:5555
127.0.0.0/8
::1/128
Live Restore Enabled: false
Firewall Backend: iptables
Additional Info
No response
Description
docker context importhandles zip archives where themeta.jsonbranchcorrectly limits decompressed reads with
limitedReader(10MB cap), but thetls/branch uses a bareio.ReadAll(f)with no size limit. Since the raw zipfile is limited to 10MB compressed, but zip compression can achieve 1000:1+
ratios on repetitive data, a crafted zip can contain a TLS entry that
decompresses to gigabytes, crashing the Docker CLI with OOM.
There is inconsistency between two branches in the same function:
Reproduce
import os
compressed_size = os.path.getsize(OUTPUT)
print(f"Compressed zip size: {compressed_size / 1024:.1f} KB")
print(f"Decompression ratio: {BOMB_SIZE / compressed_size:.0f}:1")
print(f"Output: {OUTPUT}")
print()
print("Run: docker context import bomb-test /tmp/context-bomb.zip")
Expected behavior
There should be checks on size on both branches.
docker version
lient: Docker Engine - Community Version: 29.3.1 API version: 1.53 (downgraded from 1.54) Go version: go1.26.1 Git commit: c2be9ccfc3 Built: Wed Mar 25 14:22:32 2026 OS/Arch: darwin/arm64 Context: desktop-linux Server: Docker Desktop 4.65.0 (221669) Engine: Version: 29.2.1 API version: 1.53 (minimum version 1.44) Go version: go1.25.6 Git commit: 6bc6209 Built: Mon Feb 2 17:16:47 2026 OS/Arch: linux/arm64 Experimental: false containerd: Version: v2.2.1 GitCommit: dea7da592f5d1d2b7755e3a161be07f43fad8f75 runc: Version: 1.3.4 GitCommit: v1.3.4-0-gd6d73eb8 docker-init: Version: 0.19.0 GitCommit: de40ad0docker info
Additional Info
No response