Subject: [PATCH 2/3] remote-curl: fix memory leak in show_http_message()

From: "Vaidas Pilkauskas via GitGitGadget" <[email-masked]>
Date: Wed, 26 Nov 2025 12:30:26 +0000

From: Vaidas Pilkauskas <[email-masked]>

Fix a memory leak in show_http_message() that was triggered when
displaying HTTP error messages before die(). The function would call
strbuf_reencode() which modifies the caller's strbuf in place,
allocating new memory for the re-encoded string. Since this function
is only called immediately before die(), the allocated memory was
never explicitly freed, causing leak detectors to report it.

The leak became visible when HTTP 429 rate limit retry support was
added, which introduced the HTTP_RATE_LIMITED error case. However,
the issue existed in pre-existing error paths as well
(HTTP_MISSING_TARGET, HTTP_NOAUTH, HTTP_NOMATCHPUBLICKEY) - the new
retry logic just made it more visible in tests because retries
exercise the error paths more frequently.

The leak was detected by LeakSanitizer in t5584 tests that enable
retries (maxRetries > 0). Tests with retries disabled passed because
they took a different code path or timing.

Fix this by making show_http_message() work on a local copy of the
message buffer instead of modifying the caller's buffer in place:

1. Create a local strbuf and copy the message into it
2. Perform re-encoding on the local copy if needed
3. Display the message from the local copy
4. Properly release the local copy before returning

This ensures all memory allocated by strbuf_reencode() is freed
before the function returns, even though die() is called immediately
after, eliminating the leak.

Signed-off-by: Vaidas Pilkauskas <[email-masked]>
---
 remote-curl.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/remote-curl.c b/remote-curl.c
index 5959461cd3..dd0680e5ae 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -371,6 +371,7 @@ static int show_http_message(struct strbuf *type, struct strbuf *charset,
 			     struct strbuf *msg)
 {
 	const char *p, *eol;
+	struct strbuf msgbuf = STRBUF_INIT;
 
 	/*
 	 * We only show text/plain parts, as other types are likely
@@ -378,19 +379,24 @@ static int show_http_message(struct strbuf *type, struct strbuf *charset,
 	 */
 	if (strcmp(type->buf, "text/plain"))
 		return -1;
+
+	strbuf_addbuf(&msgbuf, msg);
 	if (charset->len)
-		strbuf_reencode(msg, charset->buf, get_log_output_encoding());
+		strbuf_reencode(&msgbuf, charset->buf, get_log_output_encoding());
 
-	strbuf_trim(msg);
-	if (!msg->len)
+	strbuf_trim(&msgbuf);
+	if (!msgbuf.len) {
+		strbuf_release(&msgbuf);
 		return -1;
+	}
 
-	p = msg->buf;
+	p = msgbuf.buf;
 	do {
 		eol = strchrnul(p, '\n');
 		fprintf(stderr, "remote: %.*s\n", (int)(eol - p), p);
 		p = eol + 1;
 	} while(*eol);
+	strbuf_release(&msgbuf);
 	return 0;
 }
 
-- 
gitgitgadget