copy: prevent potential deadlock if close before fully written

We also need an additional check to avoid setting both the error and
response which can create a race where they can arrive in the receiving
thread in either order.

If we hit an error, we don't need to send the response.

> There is a condition where the registry (unexpectedly, not to spec)
> returns 201 or 204 on the put before the body is fully written. I would
> expect that the http library would issue close and could fall into a
> deadlock here. We could just read respC and call setResponse. In that
> case ErrClosedPipe would get returned and Commit shouldn't be called
> anyway.

Signed-off-by: Justin Chadwell <me@jedevc.com>
This commit is contained in:
Justin Chadwell 2023-05-11 15:44:24 +01:00
parent b48e1141eb
commit a9152ebf89

View File

@ -297,6 +297,7 @@ func (p dockerPusher) push(ctx context.Context, desc ocispec.Descriptor, ref str
err := remoteserrors.NewUnexpectedStatusErr(resp) err := remoteserrors.NewUnexpectedStatusErr(resp)
log.G(ctx).WithField("resp", resp).WithField("body", string(err.(remoteserrors.ErrUnexpectedStatus).Body)).Debug("unexpected response") log.G(ctx).WithField("resp", resp).WithField("body", string(err.(remoteserrors.ErrUnexpectedStatus).Body)).Debug("unexpected response")
pushw.setError(err) pushw.setError(err)
return
} }
pushw.setResponse(resp) pushw.setResponse(resp)
}() }()
@ -432,6 +433,8 @@ func (pw *pushWriter) Write(p []byte) (n int, err error) {
pw.Close() pw.Close()
case p := <-pw.pipeC: case p := <-pw.pipeC:
return 0, pw.replacePipe(p) return 0, pw.replacePipe(p)
case resp := <-pw.respC:
pw.setResponse(resp)
} }
} }
status.Offset += int64(n) status.Offset += int64(n)