line-log: fix several memory leaks

As described in "line-log.c" itself, the code is "leaking like a sieve". These leaks are all of rather trivial nature, so this commit plugs them without going too much into details for each of those leaks. The leaks are hit by t4211, but plugging them alone does not make the full test suite pass. The remaining leaks are unrelated to the line-log subsystem. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Sep 30, 2024 at 11:13 UTC 5ce08ed4fbb60c984f46632aaa30d8f781e57e56
1 file changed +35 -19
line-log.c
+35 -19
@@ -248,8 +248,10 @@ static void line_log_data_init(struct line_log_data *r)
248 static void line_log_data_clear(struct line_log_data *r)
249 {
250 range_set_release(&r->ranges);
251 + free(r->path);
252 if (r->pair)
253 diff_free_filepair(r->pair);
254 + diff_ranges_release(&r->diff);
255 }
256
257 static void free_line_log_data(struct line_log_data *r)
@@ -571,7 +573,8 @@ parse_lines(struct repository *r, struct commit *commit,
573 struct line_log_data *p;
574
575 for_each_string_list_item(item, args) {
574 - const char *name_part, *range_part;
576 + const char *name_part;
577 + char *range_part;
578 char *full_name;
579 struct diff_filespec *spec;
580 long begin = 0, end = 0;
@@ -615,6 +618,7 @@ parse_lines(struct repository *r, struct commit *commit,
618
619 free_filespec(spec);
620 FREE_AND_NULL(ends);
621 + free(range_part);
622 }
623
624 for (p = ranges; p; p = p->next)
@@ -760,15 +764,13 @@ static void parse_pathspec_from_ranges(struct pathspec *pathspec,
764 {
765 struct line_log_data *r;
766 struct strvec array = STRVEC_INIT;
763 - const char **paths;
767
768 for (r = range; r; r = r->next)
769 strvec_push(&array, r->path);
767 - paths = strvec_detach(&array);
770
769 - parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL, "", paths);
770 - /* strings are now owned by pathspec */
771 - free(paths);
771 + parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL, "", array.v);
772 +
773 + strvec_clear(&array);
774 }
775
776 void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args)
@@ -781,6 +783,8 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list
783 add_line_range(rev, commit, range);
784
785 parse_pathspec_from_ranges(&rev->diffopt.pathspec, range);
786 +
787 + free_line_log_data(range);
788 }
789
790 static void move_diff_queue(struct diff_queue_struct *dst,
@@ -1131,10 +1135,18 @@ static int process_all_files(struct line_log_data **range_out,
1135 while (rg && strcmp(rg->path, pair->two->path))
1136 rg = rg->next;
1137 assert(rg);
1138 + if (rg->pair)
1139 + diff_free_filepair(rg->pair);
1140 rg->pair = diff_filepair_dup(queue->queue[i]);
1141 + diff_ranges_release(&rg->diff);
1142 memcpy(&rg->diff, pairdiff, sizeof(struct diff_ranges));
1143 + FREE_AND_NULL(pairdiff);
1144 + }
1145 +
1146 + if (pairdiff) {
1147 + diff_ranges_release(pairdiff);
1148 + free(pairdiff);
1149 }
1137 - free(pairdiff);
1150 }
1151
1152 return changed;
@@ -1212,12 +1224,13 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
1224 struct commit_list *p;
1225 int i;
1226 int nparents = commit_list_count(commit->parents);
1227 + int ret;
1228
1229 if (nparents > 1 && rev->first_parent_only)
1230 nparents = 1;
1231
1232 ALLOC_ARRAY(diffqueues, nparents);
1220 - ALLOC_ARRAY(cand, nparents);
1233 + CALLOC_ARRAY(cand, nparents);
1234 ALLOC_ARRAY(parents, nparents);
1235
1236 p = commit->parents;
@@ -1229,7 +1242,6 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
1242
1243 for (i = 0; i < nparents; i++) {
1244 int changed;
1232 - cand[i] = NULL;
1245 changed = process_all_files(&cand[i], rev, &diffqueues[i], range);
1246 if (!changed) {
1247 /*
@@ -1237,13 +1249,10 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
1249 * don't follow any other path in history
1250 */
1251 add_line_range(rev, parents[i], cand[i]);
1240 - clear_commit_line_range(rev, commit);
1252 commit_list_append(parents[i], &commit->parents);
1242 - free(parents);
1243 - free(cand);
1244 - free_diffqueues(nparents, diffqueues);
1245 - /* NEEDSWORK leaking like a sieve */
1246 - return 0;
1253 +
1254 + ret = 0;
1255 + goto out;
1256 }
1257 }
1258
@@ -1251,18 +1260,25 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
1260 * No single parent took the blame. We add the candidates
1261 * from the above loop to the parents.
1262 */
1254 - for (i = 0; i < nparents; i++) {
1263 + for (i = 0; i < nparents; i++)
1264 add_line_range(rev, parents[i], cand[i]);
1256 - }
1265
1266 + ret = 1;
1267 +
1268 +out:
1269 clear_commit_line_range(rev, commit);
1270 free(parents);
1271 + for (i = 0; i < nparents; i++) {
1272 + if (!cand[i])
1273 + continue;
1274 + line_log_data_clear(cand[i]);
1275 + free(cand[i]);
1276 + }
1277 free(cand);
1278 free_diffqueues(nparents, diffqueues);
1262 - return 1;
1279 + return ret;
1280
1281 /* NEEDSWORK evil merge detection stuff */
1265 - /* NEEDSWORK leaking like a sieve */
1282 }
1283
1284 int line_log_process_ranges_arbitrary_commit(struct rev_info *rev, struct commit *commit)