negotiator/default: avoid stack overflow

mark_common() in negotiator/default.c may overflow the stack due to recursive function calls. Avoid this by instead recursing using a heap-allocated data structure. This is the same case as 4654134976f (negotiator/skipping: avoid stack overflow, 2022-10-25) Reported-by: Xin Xing <xingxin.xx@bytedance.com> Signed-off-by: Han Xin <hanxin.hx@bytedance.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Han Xin committed Apr 26, 2023 at 21:15 UTC 8e21ff5edb305bcfc12fc782f1c92542c427b624
1 file changed +29 -10
negotiator/default.c
+29 -10
@@ -55,30 +55,49 @@ static int clear_marks(const char *refname, const struct object_id *oid,
55 static void mark_common(struct negotiation_state *ns, struct commit *commit,
56 int ancestors_only, int dont_parse)
57 {
58 - if (commit != NULL && !(commit->object.flags & COMMON)) {
59 - struct object *o = (struct object *)commit;
58 + struct prio_queue queue = { NULL };
59 +
60 + if (!commit || (commit->object.flags & COMMON))
61 + return;
62 +
63 + prio_queue_put(&queue, commit);
64 + if (!ancestors_only) {
65 + commit->object.flags |= COMMON;
66
61 - if (!ancestors_only)
62 - o->flags |= COMMON;
67 + if ((commit->object.flags & SEEN) && !(commit->object.flags & POPPED))
68 + ns->non_common_revs--;
69 + }
70 + while ((commit = prio_queue_get(&queue))) {
71 + struct object *o = (struct object *)commit;
72
73 if (!(o->flags & SEEN))
74 rev_list_push(ns, commit, SEEN);
75 else {
76 struct commit_list *parents;
77
69 - if (!ancestors_only && !(o->flags & POPPED))
70 - ns->non_common_revs--;
78 if (!o->parsed && !dont_parse)
79 if (repo_parse_commit(the_repository, commit))
73 - return;
80 + continue;
81
82 for (parents = commit->parents;
83 parents;
77 - parents = parents->next)
78 - mark_common(ns, parents->item, 0,
79 - dont_parse);
84 + parents = parents->next) {
85 + struct commit *p = parents->item;
86 +
87 + if (p->object.flags & COMMON)
88 + continue;
89 +
90 + p->object.flags |= COMMON;
91 +
92 + if ((p->object.flags & SEEN) && !(p->object.flags & POPPED))
93 + ns->non_common_revs--;
94 +
95 + prio_queue_put(&queue, parents->item);
96 + }
97 }
98 }
99 +
100 + clear_prio_queue(&queue);
101 }
102
103 /*