From d9651b6f2390165cf310f7608f0a7c6085be522a Mon Sep 17 00:00:00 2001 From: Marius Goetze Date: Tue, 4 Feb 2025 16:42:01 +0100 Subject: [PATCH] fix: fix memory leak on failed http requests In many error cases the http response body was not closed as the `defer` statement was only executed after the error handling. Thus the connection can't be reused. This caused the creation of many goroutines, massively increasing the memory consumption. A prominent scenario is if a CSAF provider does not provide both sha256 and sha512 checksums. --- cmd/csaf_downloader/downloader.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/cmd/csaf_downloader/downloader.go b/cmd/csaf_downloader/downloader.go index 02549ed8..484d5117 100644 --- a/cmd/csaf_downloader/downloader.go +++ b/cmd/csaf_downloader/downloader.go @@ -484,6 +484,16 @@ nextAdvisory: "error", err) continue } + responseBody, err := io.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + stats.downloadFailed++ + errorCh <- csafErrs.ErrNetwork{Message: fmt.Sprintf("can't read response body containing CSAF document %s from URL %s: %v", filename, file.URL(), err)} + slog.Warn("Cannot read response body", + "url", file.URL(), + "error", err) + continue + } if resp.StatusCode != http.StatusOK { switch { @@ -548,8 +558,7 @@ nextAdvisory: var doc any if err := func() error { - defer resp.Body.Close() - tee := io.TeeReader(resp.Body, hasher) + tee := io.TeeReader(bytes.NewReader(responseBody), hasher) return json.NewDecoder(tee).Decode(&doc) }(); err != nil { stats.downloadFailed++ @@ -766,11 +775,11 @@ func loadSignature(client util.Client, p string) (*crypto.PGPSignature, []byte, if err != nil { return nil, nil, err } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, nil, fmt.Errorf( "fetching signature from '%s' failed: %s (%d)", p, resp.Status, resp.StatusCode) } - defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { return nil, nil, err @@ -787,11 +796,11 @@ func loadHash(client util.Client, p string) ([]byte, []byte, error) { if err != nil { return nil, nil, err } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, nil, fmt.Errorf( "fetching hash from '%s' failed: %s (%d)", p, resp.Status, resp.StatusCode) } - defer resp.Body.Close() var data bytes.Buffer tee := io.TeeReader(resp.Body, &data) hash, err := util.HashFromReader(tee)