3.7 KiB
BuildKit (Docker) — CWE-407 Disclosure Brief
2026-03-27 · Patch available — awaiting upstream merge
Finding
One O(n) defect in BuildKit's remote cache storage link lookup. Patched. Patch ready for upstream review. BuildKit is Docker's next-generation image build engine — used in all docker buildx builds and Docker Desktop.
The Defect
buildkit-0001 (PATCHED — MEDIUM): cache/remotecache/v1/cachestorage.go:244
// In HasLink() — called during remote cache record lookup:
func (cs *cacheStorage) HasLink(id string, desc ocispec.Descriptor, target string) bool {
links, ok := cs.links[id]
if !ok {
return false
}
return slices.Contains(links, target) // O(L) — slices.Contains() linear scan
}
links is a []string slice. slices.Contains() performs a linear scan over the link list for every cache record lookup. With L links per cache record: O(L) per HasLink() call.
HasLink() is called during remote cache resolution — the phase where BuildKit checks its remote cache (registry-backed or inline cache) to determine what layers can be reused. For each blob in the cache, all links must be checked.
Complexity Proof
For L links per cache record and C cache records checked during a build:
- Per record: O(L)
slices.Contains()scan - Total: O(C × L)
At C=1,000 cache records, L=50 links per record: defective=50,000 comparisons, fixed=1,000 (map-based lookup). 50× op reduction.
The fix converts []string to map[string]struct{} for O(1) HasLink() lookup.
Impact
BuildKit is Docker's image build engine — used by:
- Every
docker buildx buildcommand (the modern Docker build interface) - Docker Desktop (millions of installs)
- GitHub Actions Docker build steps
- GitLab CI Docker builds
- Kubernetes pod image builds
Remote caching is a key BuildKit feature: it allows build layers to be pulled from a registry cache (Docker Hub, AWS ECR, GCR, etc.) instead of rebuilt from scratch. The HasLink() function is in the critical path of cache resolution — called for every potential cache hit during a build.
Large Dockerfiles with many layers, complex multi-stage builds, and monorepo build systems with shared layers all maximize C. Builds with complex layer dependency graphs (many links per cache record) maximize L. These are exactly the builds where remote caching provides the most value — and where this defect has the most impact.
The Fix
Replace []string links with map[string]struct{} for O(1) lookup:
// Before
links []string
// ...
return slices.Contains(links, target) // O(L) linear scan
// After
// CWE-407 fix: map[string]struct{} for O(1) HasLink() instead of O(L) slices.Contains().
links map[string]struct{}
// ...
_, ok := links[target]
return ok
Link insertion becomes links[target] = struct{}{} — O(1). No behavioral change.
Patch
Fix available: defects/buildkit/patch/buildkit-0001-cache-links-map.patch
Single-field type change in cache/remotecache/v1/cachestorage.go. Link initialization and insertion updated to use map operations.
Unit test: O(L) → O(1) per HasLink() confirmed. Remote cache resolution speedup measurable on large multi-stage builds.
What We Ask
A patch is ready for review.
- Confirm receipt and assign a GitHub issue reference (moby/buildkit).
- Assess severity — buildkit-0001 fires on every remote cache record lookup during Docker builds; large builds with many layers and complex caches are worst case.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
- We will credit the BuildKit/Moby team in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.