* Re: [PATCH] remote: plug memory leaks
2026-07-25 0:43 [PATCH] remote: plug memory leaks Junio C Hamano
@ 2026-07-25 1:57 ` Junio C Hamano
2026-07-25 16:03 ` [PATCH v2] " Junio C Hamano
1 sibling, 0 replies; 5+ messages in thread
From: Junio C Hamano @ 2026-07-25 1:57 UTC (permalink / raw)
To: git, Ted Nyman; +Cc: Éric NICOLAS
Junio C Hamano <gitster@pobox.com> writes:
> The in-core data structure used to keep track of
> 'url.<real>.{insteadOf,pushInsteadOf} = <alias>' settings is not
> properly cleaned up when the process is done with it.
>
> Fix the rewrites_release() function to free not just the 'struct
> rewrites' instance itself, but also allocated structures that are
> pointed at by the 'struct rewrites' instance. One of the embedded
> structures holds a 'const char *' to point at a borrowed constant
> string from a configuration callback. Since the code does not
> modify this string, stop copying the value (alias URL) before
> registering it in 'struct rewrite', as nobody is freeing this
> member, to avoid leaking the extra copy.
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>
> * These are not recently introduced leaks as far as I can tell, but
> the new tests in en/submodule-insteadof-remote-match expose them.
It is unfortunately rare to see all CI jobs pass, but today is one
of those days ;-)
With this, and everything in 'seen' reported in the last edition of
the "What's cooking" report, excluding the
'tn/packfile-uri-concurrency' topic, CI passes all jobs.
https://github.com/git/git/actions/runs/30137079882/
'tn/packfile-uri-concurrency' was tentatively excluded from the
above as I made a random guess at who the culprit for the t5550
failure in
https://github.com/git/git/actions/runs/30130205851/job/89602846186
for the SHA-256 CI job was. I have merged the topic back into
'seen', and the resulting CI run for 'seen' is here:
https://github.com/git/git/actions/runs/30138777784/
It has not finished running, so we'll see how it goes.
Thanks.
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v2] remote: plug memory leaks
2026-07-25 0:43 [PATCH] remote: plug memory leaks Junio C Hamano
2026-07-25 1:57 ` Junio C Hamano
@ 2026-07-25 16:03 ` Junio C Hamano
2026-07-25 16:18 ` Jeff King
1 sibling, 1 reply; 5+ messages in thread
From: Junio C Hamano @ 2026-07-25 16:03 UTC (permalink / raw)
To: git
The in-core data structure used to keep track of
'url.<real>.{insteadOf,pushInsteadOf} = <alias>' settings is not
properly cleaned up when the process is done with it.
'struct rewrites' is embedded in 'remote_state' and serves as the
top level of the rewrite data. This holds an array of a variable
number of pointers to 'struct rewrite' allocated individually on the
heap. Each 'struct rewrite' holds a '.base' string and an array of
'struct counted_string' called '.instead_of', which is allocated
contiguously on the heap. Each 'struct counted_string' has a
pointer to a string allocated on the heap.
Amid these pointers, rewrites_release() fails to free everything
other than 'struct rewrite''s '.base' member and the 'struct rewrite'
instances themselves.
Fix rewrites_release() to also free the contiguous array storing
'.instead_of', the string pointers within each '.instead_of' element,
and each 'struct rewrite' instance individually allocated on the heap.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
* The initial iteration relied on the assumption that strings
borrowed from the configset subsystem will not go away, attempting
to plug the leak of 'instead_of[n].s' pointers without making
copies. However, it turns out that all existing users other than
a select few make copies and do not rely on that assumption. In
this version, I decided to simply follow suit, which might be
slightly inefficient but is vastly safer.
---
remote.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/remote.c b/remote.c
index a664cd166a..6c84adb36a 100644
--- a/remote.c
+++ b/remote.c
@@ -304,8 +304,15 @@ static struct rewrite *make_rewrite(struct rewrites *r,
static void rewrites_release(struct rewrites *r)
{
- for (int i = 0; i < r->rewrite_nr; i++)
- free((char *)r->rewrite[i]->base);
+ for (int i = 0; i < r->rewrite_nr; i++) {
+ struct rewrite *rewrite = r->rewrite[i];
+
+ free((char *)rewrite->base);
+ for (int j = 0; j < rewrite->instead_of_nr; j++)
+ free((char *)rewrite->instead_of[j].s);
+ free(rewrite->instead_of);
+ free(rewrite);
+ }
free(r->rewrite);
memset(r, 0, sizeof(*r));
}
Range-diff:
1: 19a305bd22 ! 1: 3bd8668117 remote: plug memory leaks
@@ Commit message
'url.<real>.{insteadOf,pushInsteadOf} = <alias>' settings is not
properly cleaned up when the process is done with it.
- Fix the rewrites_release() function to free not just the 'struct
- rewrites' instance itself, but also allocated structures that are
- pointed at by the 'struct rewrites' instance. One of the embedded
- structures holds a 'const char *' to point at a borrowed constant
- string from a configuration callback. Since the code does not
- modify this string, stop copying the value (alias URL) before
- registering it in 'struct rewrite', as nobody is freeing this
- member, to avoid leaking the extra copy.
+ 'struct rewrites' is embedded in 'remote_state' and serves as the
+ top level of the rewrite data. This holds an array of a variable
+ number of pointers to 'struct rewrite' allocated individually on the
+ heap. Each 'struct rewrite' holds a '.base' string and an array of
+ 'struct counted_string' called '.instead_of', which is allocated
+ contiguously on the heap. Each 'struct counted_string' has a
+ pointer to a string allocated on the heap.
+
+ Amid these pointers, rewrites_release() fails to free everything
+ other than 'struct rewrite''s '.base' member and the 'struct rewrite'
+ instances themselves.
+
+ Fix rewrites_release() to also free the contiguous array storing
+ '.instead_of', the string pointers within each '.instead_of' element,
+ and each 'struct rewrite' instance individually allocated on the heap.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
+ ---
+
+ * The initial iteration relied on the assumption that strings
+ borrowed from the configset subsystem will not go away, attempting
+ to plug the leak of 'instead_of[n].s' pointers without making
+ copies. However, it turns out that all existing users other than
+ a select few make copies and do not rely on that assumption. In
+ this version, I decided to simply follow suit, which might be
+ slightly inefficient but is vastly safer.
## remote.c ##
@@ remote.c: static struct rewrite *make_rewrite(struct rewrites *r,
@@ remote.c: static struct rewrite *make_rewrite(struct rewrites *r,
static void rewrites_release(struct rewrites *r)
{
- for (int i = 0; i < r->rewrite_nr; i++)
+- free((char *)r->rewrite[i]->base);
+ for (int i = 0; i < r->rewrite_nr; i++) {
- free((char *)r->rewrite[i]->base);
-+ free(r->rewrite[i]->instead_of);
-+ free(r->rewrite[i]);
++ struct rewrite *rewrite = r->rewrite[i];
++
++ free((char *)rewrite->base);
++ for (int j = 0; j < rewrite->instead_of_nr; j++)
++ free((char *)rewrite->instead_of[j].s);
++ free(rewrite->instead_of);
++ free(rewrite);
+ }
free(r->rewrite);
memset(r, 0, sizeof(*r));
}
-@@ remote.c: static int handle_config(const char *key, const char *value,
- return config_error_nonbool(key);
- rewrite = make_rewrite(&remote_state->rewrites, name,
- namelen);
-- add_instead_of(rewrite, xstrdup(value));
-+ add_instead_of(rewrite, value);
- } else if (!strcmp(subkey, "pushinsteadof")) {
- if (!value)
- return config_error_nonbool(key);
- rewrite = make_rewrite(&remote_state->rewrites_push,
- name, namelen);
-- add_instead_of(rewrite, xstrdup(value));
-+ add_instead_of(rewrite, value);
- }
- }
-
--
2.55.0-570-g266ec51bf1
^ permalink raw reply related [flat|nested] 5+ messages in thread