sparse-checkout: refactor temporary sparse_checkout_patterns

In update_working_directory(), we take in a pattern_list, attach it to the repository index by assigning it to index->sparse_checkout_patterns, and then call unpack_trees. Afterwards, we remove it by setting index->sparse_checkout_patterns back to NULL. But there are two possible leaks here: 1. If the index already had a populated sparse_checkout_patterns, we've obliterated it. We can fix this by saving and restoring it, rather than always setting it back to NULL. 2. We may call the function with a NULL pattern_list, expecting it to use the on-disk sparse file. In that case, the index routines will lazy-load the sparse patterns automatically. But now at the end of the function when we restore the patterns, we'll leak those lazy-loaded ones! We can fix this by freeing the pattern list before overwriting its pointer whenever it does not match what was passed in (in practice this should only happen when the passed-in list is NULL, but this is erring on the defensive side). Together these remove 48 indirect leaks found in t1091. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Jun 4, 2024 at 06:13 UTC a14d49ca846df43f706691b6219140673a65500b
1 file changed +8 -1
builtin/sparse-checkout.c
+8 -1
@@ -205,11 +205,13 @@ static int update_working_directory(struct pattern_list *pl)
205 struct unpack_trees_options o;
206 struct lock_file lock_file = LOCK_INIT;
207 struct repository *r = the_repository;
208 + struct pattern_list *old_pl;
209
210 /* If no branch has been checked out, there are no updates to make. */
211 if (is_index_unborn(r->index))
212 return UPDATE_SPARSITY_SUCCESS;
213
214 + old_pl = r->index->sparse_checkout_patterns;
215 r->index->sparse_checkout_patterns = pl;
216
217 memset(&o, 0, sizeof(o));
@@ -241,7 +243,12 @@ static int update_working_directory(struct pattern_list *pl)
243
244 clean_tracked_sparse_directories(r);
245
244 - r->index->sparse_checkout_patterns = NULL;
246 + if (r->index->sparse_checkout_patterns != pl) {
247 + clear_pattern_list(r->index->sparse_checkout_patterns);
248 + FREE_AND_NULL(r->index->sparse_checkout_patterns);
249 + }
250 + r->index->sparse_checkout_patterns = old_pl;
251 +
252 return result;
253 }
254