-
Notifications
You must be signed in to change notification settings - Fork 629
Description
summary
go-containerregistry reads multiple untrusted http response bodies using io.ReadAll without an explicit size limit. a malicious (or compromised/misconfigured) registry endpoint, or a registry-controlled auth/token endpoint, can return an arbitrarily large response body and force unbounded memory allocation in the client process, leading to memory exhaustion and potential oom termination.
this is the same invariant violation across multiple callsites, so fixing only one path may leave others reachable.
note: this is being filed as a public issue after google bug hunters marked the report "won't fix (infeasible)" as a security escalation on 2026-01-30, and explicitly suggested public disclosure on github.
impact
- availability: memory exhaustion in the client process (potential oom kill depending on limits)
- attacker requirements: attacker controls the registry endpoint the client is configured to talk to, or an auth/token endpoint used during registry access
- scope: client-side dos (no confidentiality/integrity impact expected)
affected versions
confirmed at:
v0.20.3(commitc4dd792fa06c1f8b780ad90c8ab4f38b4eac05bd)
also confirmed present in:
v0.20.7(sameio.ReadAllcallsites)
callsites (unbounded reads)
pkg/v1/remote/referrers.go:70(Referrers/fetchReferrers) —io.ReadAll(resp.Body)pkg/v1/remote/transport/error.go:164(CheckError) —io.ReadAll(resp.Body)pkg/v1/remote/transport/error.go:188(retryError) —io.ReadAll(resp.Body)pkg/v1/remote/transport/bearer.go:362(refreshOauth) —io.ReadAll(resp.Body)pkg/v1/remote/transport/bearer.go:406(refreshBasic) —io.ReadAll(resp.Body)pkg/v1/remote/image.go:129(RawConfigFile) —io.ReadAll(body)
suggested remediation
replace io.ReadAll on untrusted http bodies with a bounded read that:
- enforces a per-callsite maximum (e.g. a few mib for errors/token/referrers; higher but still bounded for config blobs)
- detects truncation (read
limit+1) and returns a clear “response body too large” error
example helper:
func readAllLimit(r io.Reader, max int64) ([]byte, error) {
lr := io.LimitReader(r, max+1)
b, err := io.ReadAll(lr)
if err != nil {
return nil, err
}
if int64(len(b)) > max {
return nil, fmt.Errorf("response body exceeds %d bytes", max)
}
return b, nil
}