Description
In pkg/backend/push.go:175-189, src.PullBlob() returns an io.ReadCloser that is never explicitly closed — on both success and error paths.
The reader is wrapped with io.NopCloser(reader) before passing to dst.Blobs().Push(), so even when push closes the wrapper, the original content ReadCloser is not closed.
Code
content, err := src.PullBlob(ctx, repo, desc.Digest.String())
if err \!= nil {
return err // content not closed
}
reader := pb.Add(prompt, desc.Digest.String(), desc.Size, content)
// io.NopCloser means dst closing the wrapper does NOT close content
if err := dst.Blobs().Push(ctx, desc, io.NopCloser(reader)); err \!= nil {
pb.Abort(desc.Digest.String(), err)
return err // content not closed
}
// success path: content also not closed
Fix
Add defer content.Close() immediately after the nil-error check.
Severity
Critical — resource leak under normal operation, not just error paths.
Description
In
pkg/backend/push.go:175-189,src.PullBlob()returns anio.ReadCloserthat is never explicitly closed — on both success and error paths.The reader is wrapped with
io.NopCloser(reader)before passing todst.Blobs().Push(), so even when push closes the wrapper, the originalcontentReadCloser is not closed.Code
Fix
Add
defer content.Close()immediately after the nil-error check.Severity
Critical — resource leak under normal operation, not just error paths.