Skip to content

Commit 6f54d00

Browse files
pks-tgitster
authored andcommitted
fetch-pack: fix leaking sought refs
When calling `fetch_pack()` the caller is expected to pass in a set of sought-after refs that they want to fetch. This array gets massaged to not contain duplicate entries, which is done by replacing duplicate refs with `NULL` pointers. This modifies the caller-provided array, and in case we do unset any pointers the caller now loses track of that ref and cannot free it anymore. Now the obvious fix would be to not only unset these pointers, but to also free their contents. But this doesn't work because callers continue to use those refs. Another potential solution would be to copy the array in `fetch_pack()` so that we dont modify the caller-provided one. But that doesn't work either because the NULL-ness of those entries is used by callers to skip over ref entries that we didn't even try to fetch in `report_unmatched_refs()`. Instead, we make it the responsibility of our callers to duplicate these arrays as needed. It ain't pretty, but it works to plug the memory leak. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 61133e6 commit 6f54d00

File tree

3 files changed

+21
-2
lines changed

3 files changed

+21
-2
lines changed

builtin/fetch-pack.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ int cmd_fetch_pack(int argc,
5353
struct ref *fetched_refs = NULL, *remote_refs = NULL;
5454
const char *dest = NULL;
5555
struct ref **sought = NULL;
56+
struct ref **sought_to_free = NULL;
5657
int nr_sought = 0, alloc_sought = 0;
5758
int fd[2];
5859
struct string_list pack_lockfiles = STRING_LIST_INIT_DUP;
@@ -243,6 +244,13 @@ int cmd_fetch_pack(int argc,
243244
BUG("unknown protocol version");
244245
}
245246

247+
/*
248+
* Create a shallow copy of `sought` so that we can free all of its entries.
249+
* This is because `fetch_pack()` will modify the array to evict some
250+
* entries, but won't free those.
251+
*/
252+
DUP_ARRAY(sought_to_free, sought, nr_sought);
253+
246254
fetched_refs = fetch_pack(&args, fd, remote_refs, sought, nr_sought,
247255
&shallow, pack_lockfiles_ptr, version);
248256

@@ -280,7 +288,8 @@ int cmd_fetch_pack(int argc,
280288
oid_to_hex(&ref->old_oid), ref->name);
281289

282290
for (size_t i = 0; i < nr_sought; i++)
283-
free_one_ref(sought[i]);
291+
free_one_ref(sought_to_free[i]);
292+
free(sought_to_free);
284293
free(sought);
285294
free_refs(fetched_refs);
286295
free_refs(remote_refs);

t/t5700-protocol-v1.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export GIT_TEST_PROTOCOL_VERSION
1111
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
1212
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
1313

14+
TEST_PASSES_SANITIZE_LEAK=true
1415
. ./test-lib.sh
1516

1617
# Test protocol v1 with 'git://' transport

transport.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ static int fetch_refs_via_pack(struct transport *transport,
414414
struct git_transport_data *data = transport->data;
415415
struct ref *refs = NULL;
416416
struct fetch_pack_args args;
417-
struct ref *refs_tmp = NULL;
417+
struct ref *refs_tmp = NULL, **to_fetch_dup = NULL;
418418

419419
memset(&args, 0, sizeof(args));
420420
args.uploadpack = data->options.uploadpack;
@@ -477,6 +477,14 @@ static int fetch_refs_via_pack(struct transport *transport,
477477
goto cleanup;
478478
}
479479

480+
/*
481+
* Create a shallow copy of `sought` so that we can free all of its entries.
482+
* This is because `fetch_pack()` will modify the array to evict some
483+
* entries, but won't free those.
484+
*/
485+
DUP_ARRAY(to_fetch_dup, to_fetch, nr_heads);
486+
to_fetch = to_fetch_dup;
487+
480488
refs = fetch_pack(&args, data->fd,
481489
refs_tmp ? refs_tmp : transport->remote_refs,
482490
to_fetch, nr_heads, &data->shallow,
@@ -500,6 +508,7 @@ static int fetch_refs_via_pack(struct transport *transport,
500508
ret = -1;
501509
data->conn = NULL;
502510

511+
free(to_fetch_dup);
503512
free_refs(refs_tmp);
504513
free_refs(refs);
505514
list_objects_filter_release(&args.filter_options);

0 commit comments

Comments
 (0)