Message ID | patch-v2-2.3-1d5f5e9fff0-20211007T094019Z-avarab@gmail.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | unpack-trees: memory-leak fixes | expand |
On Thu, Oct 7, 2021 at 2:46 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote: > > Restructure code that's mostly added in 9055e401dd6 (sequencer: > introduce new commands to reset the revision, 2018-04-25) to avoid > code duplication, and to make freeing other resources easier in a > subsequent commit. Since the first goto you add precedes the call to setup_unpack_trees_porcelain(), the next commit will introduce a case where some code calls clear_unpack_trees_porcelain() without having first called setup_unpack_trees_porcelain(). With the current code that is fine since you do initialize unpack_trees_opts to { 0 } first, and clear_unpack_trees_porcelain() doesn't rely on any special setup, but I wonder if the naming similarity of the two functions might lead to future authors to presume they are always called in pairs. I wonder if we should rename clear_unpack_trees_porcelain() to e.g. unpack_trees_clear() just to avoid this problem, and then include the other bits of your previous round to just make all callers of unpack_trees() call unpack_trees_clear() when they are done. Or, maybe we could just make unpack_trees() call unpack_trees_clear() automatically and remove that responsibility from callers...except that merge-recursive is weird and special and would need a new function (unpack_trees_without_automatic_cleanup() or something like that?) since it's the one caller that needs to use unpack_trees_options data after calling unpack_trees(). So, multiple competing ideas here, and all for theoretical future code safety. As such, if any of it sounds like too much of a pain, I'm fine with punting on it. > It's safe to initialize "tree_desc" to be zero'd out in order to > unconditionally free desc.buffer, it won't be initialized on the first > couple of "goto"'s. > > There are three earlier "return"'s in this function that I'm not > bothering to covert, s/covert/convert/ > those don't need to rollback anything, They aren't currently rolling back the lockfile right now, but I think they should. Those error cases are pretty unlikely to happen (hard disk full?), but if they did, they'd leave the lock file in place. Granted, that's a pre-existing problem rather than a problem introduced by your patch, so it doesn't need to be part of this series, but the commit message should at least be tweaked. > or free any resources True. > , so let's leave, even though they could safely "goto > cleanup" as well. > > Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> > --- > sequencer.c | 32 +++++++++++++------------------- > 1 file changed, 13 insertions(+), 19 deletions(-) > > diff --git a/sequencer.c b/sequencer.c > index 6872b7b00a4..457eba4ab10 100644 > --- a/sequencer.c > +++ b/sequencer.c > @@ -3650,7 +3650,7 @@ static int do_reset(struct repository *r, > struct strbuf ref_name = STRBUF_INIT; > struct object_id oid; > struct lock_file lock = LOCK_INIT; > - struct tree_desc desc; > + struct tree_desc desc = { 0 }; > struct tree *tree; > struct unpack_trees_options unpack_tree_opts; > int ret = 0; > @@ -3684,10 +3684,8 @@ static int do_reset(struct repository *r, > strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name); > if (get_oid(ref_name.buf, &oid) && > get_oid(ref_name.buf + strlen("refs/rewritten/"), &oid)) { > - error(_("could not read '%s'"), ref_name.buf); > - rollback_lock_file(&lock); > - strbuf_release(&ref_name); > - return -1; > + ret = error(_("could not read '%s'"), ref_name.buf); > + goto cleanup; > } > } > > @@ -3703,24 +3701,18 @@ static int do_reset(struct repository *r, > init_checkout_metadata(&unpack_tree_opts.meta, name, &oid, NULL); > > if (repo_read_index_unmerged(r)) { > - rollback_lock_file(&lock); > - strbuf_release(&ref_name); > - return error_resolve_conflict(_(action_name(opts))); > + ret = error_resolve_conflict(_(action_name(opts))); > + goto cleanup; > } > > if (!fill_tree_descriptor(r, &desc, &oid)) { > - error(_("failed to find tree of %s"), oid_to_hex(&oid)); > - rollback_lock_file(&lock); > - free((void *)desc.buffer); > - strbuf_release(&ref_name); > - return -1; > + ret = error(_("failed to find tree of %s"), oid_to_hex(&oid)); > + goto cleanup; > } > > if (unpack_trees(1, &desc, &unpack_tree_opts)) { > - rollback_lock_file(&lock); > - free((void *)desc.buffer); > - strbuf_release(&ref_name); > - return -1; > + ret = -1; > + goto cleanup; > } > > tree = parse_tree_indirect(&oid); > @@ -3728,13 +3720,15 @@ static int do_reset(struct repository *r, > > if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) > ret = error(_("could not write index")); > - free((void *)desc.buffer); > > if (!ret) > ret = update_ref(reflog_message(opts, "reset", "'%.*s'", > len, name), "HEAD", &oid, > NULL, 0, UPDATE_REFS_MSG_ON_ERR); > - > +cleanup: > + free((void *)desc.buffer); > + if (ret < 0) > + rollback_lock_file(&lock); > strbuf_release(&ref_name); > return ret; > } > -- > 2.33.0.1446.g6af949f83bd >
diff --git a/sequencer.c b/sequencer.c index 6872b7b00a4..457eba4ab10 100644 --- a/sequencer.c +++ b/sequencer.c @@ -3650,7 +3650,7 @@ static int do_reset(struct repository *r, struct strbuf ref_name = STRBUF_INIT; struct object_id oid; struct lock_file lock = LOCK_INIT; - struct tree_desc desc; + struct tree_desc desc = { 0 }; struct tree *tree; struct unpack_trees_options unpack_tree_opts; int ret = 0; @@ -3684,10 +3684,8 @@ static int do_reset(struct repository *r, strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name); if (get_oid(ref_name.buf, &oid) && get_oid(ref_name.buf + strlen("refs/rewritten/"), &oid)) { - error(_("could not read '%s'"), ref_name.buf); - rollback_lock_file(&lock); - strbuf_release(&ref_name); - return -1; + ret = error(_("could not read '%s'"), ref_name.buf); + goto cleanup; } } @@ -3703,24 +3701,18 @@ static int do_reset(struct repository *r, init_checkout_metadata(&unpack_tree_opts.meta, name, &oid, NULL); if (repo_read_index_unmerged(r)) { - rollback_lock_file(&lock); - strbuf_release(&ref_name); - return error_resolve_conflict(_(action_name(opts))); + ret = error_resolve_conflict(_(action_name(opts))); + goto cleanup; } if (!fill_tree_descriptor(r, &desc, &oid)) { - error(_("failed to find tree of %s"), oid_to_hex(&oid)); - rollback_lock_file(&lock); - free((void *)desc.buffer); - strbuf_release(&ref_name); - return -1; + ret = error(_("failed to find tree of %s"), oid_to_hex(&oid)); + goto cleanup; } if (unpack_trees(1, &desc, &unpack_tree_opts)) { - rollback_lock_file(&lock); - free((void *)desc.buffer); - strbuf_release(&ref_name); - return -1; + ret = -1; + goto cleanup; } tree = parse_tree_indirect(&oid); @@ -3728,13 +3720,15 @@ static int do_reset(struct repository *r, if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) ret = error(_("could not write index")); - free((void *)desc.buffer); if (!ret) ret = update_ref(reflog_message(opts, "reset", "'%.*s'", len, name), "HEAD", &oid, NULL, 0, UPDATE_REFS_MSG_ON_ERR); - +cleanup: + free((void *)desc.buffer); + if (ret < 0) + rollback_lock_file(&lock); strbuf_release(&ref_name); return ret; }
Restructure code that's mostly added in 9055e401dd6 (sequencer: introduce new commands to reset the revision, 2018-04-25) to avoid code duplication, and to make freeing other resources easier in a subsequent commit. It's safe to initialize "tree_desc" to be zero'd out in order to unconditionally free desc.buffer, it won't be initialized on the first couple of "goto"'s. There are three earlier "return"'s in this function that I'm not bothering to covert, those don't need to rollback anything, or free any resources, so let's leave, even though they could safely "goto cleanup" as well. Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> --- sequencer.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-)