From: Elijah Newren Date: Fri, 11 Sep 2026 18:06:33 GMT Subject: Re: [PATCH v2 1/3] merge-ll: use strbuf to read back external merge result Message-ID: In-Reply-To: <20260911171124.GA1610200@coredump.intra.peff.net> On Fri, Sep 11, 2026 at 10:11 AM Jeff King wrote: > > After the external merge runs, we read the file back into a heap buffer. > This ancient code does it by hand, but these days we can make the code > shorter and less error prone by using strbuf_read_file(). > > It's not quite a one-liner replacement, because we have to copy the > pointer and size into an mmbuffer_t. Two things to note there: > > 1. We can't just pass result->size to strbuf_detach(), since the > former uses long instead of size_t (something that we'd ideally fix > in the long run, but is way out of scope here). > > 2. We can leave result untouched on error; we zero it at the top of > the function (confusingly we may still return LL_MERGE_OK and a > NULL result if we hit an I/O error, but that is how the function > has always behaved, and callers know to check for NULL). > > Signed-off-by: Jeff King > --- > Not strictly needed for the rest of the series, but it felt like a > cleanup worth doing, and it conflicts textually. > > merge-ll.c | 22 +++++++--------------- > 1 file changed, 7 insertions(+), 15 deletions(-) > > diff --git a/merge-ll.c b/merge-ll.c > index ef5287dee8..5b6af15e23 100644 > --- a/merge-ll.c > +++ b/merge-ll.c > @@ -201,8 +201,8 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn, > struct strbuf cmd = STRBUF_INIT; > const char *format = fn->cmdline; > struct child_process child = CHILD_PROCESS_INIT; > - int status, fd, i; > - struct stat st; > + int status, i; > + struct strbuf result_buf = STRBUF_INIT; > enum ll_merge_result ret; > assert(opts); > > @@ -241,20 +241,12 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn, > child.use_shell = 1; > strvec_push(&child.args, cmd.buf); > status = run_command(&child); > - fd = open(temp[1], O_RDONLY); > - if (fd < 0) > - goto bad; > - if (fstat(fd, &st)) > - goto close_bad; > - result->size = st.st_size; > - result->ptr = xmallocz(result->size); > - if (read_in_full(fd, result->ptr, result->size) != result->size) { > - FREE_AND_NULL(result->ptr); > - result->size = 0; > + > + if (strbuf_read_file(&result_buf, temp[1], 0) >= 0) { > + result->size = result_buf.len; > + result->ptr = strbuf_detach(&result_buf, NULL); I know the type mismatch is pre-existing, but the order makes the new behavior different. On LLP64, assuming the usual wraparound, a result of LONG_MAX + 101 narrows to the negative value LONG_MIN + 100 . The old code narrows before xmallocz() , so it requests an impossibly large allocation and dies. The new code allocates the actual buffer first, then records a negative size; callers converting that size back to size_t could read past the allocation. Would a simple fail-fast make sense? if (result_buf.len > LONG_MAX) die(_("external merge result is too large")); > } > - close_bad: > - close(fd); > - bad: > + > for (i = 0; i < 3; i++) > unlink_or_warn(temp[i]); > strbuf_release(&cmd); > -- > 2.56.0.rc0.314.g7a874b6915 Otherwise, looks nice.