Re: [PATCH v2] bisect--helper: plug strvec leak
- From
Jeff King <peff@peff.net>
- Date
- Oct 11, 2022, 13:20 UTC
- Message-ID
- <Y0VtkmNwjKcXcemP@coredump.intra.peff.net>
- In-Reply-To
- <xmqq35buykz1.fsf@gitster.g>
On Mon, Oct 10, 2022 at 10:42:42PM -0700, Junio C Hamano wrote:
Show 23 quoted lines
> >> - struct strvec argv = STRVEC_INIT;
> >> + const char *argv[] = { "checkout", start_head.buf,
> >> + "--", NULL };
> >>
> >> - strvec_pushl(&argv, "checkout", start_head.buf,
> >> - "--", NULL);
> >> - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
> >> + if (run_command_v_opt(argv, RUN_GIT_CMD)) {
> >
> > This is OK with me, but note that one thing we lose is compiler
> > protection that we remembered the trailing NULL pointer in the argv
> > array (whereas strvec_pushl() has an attribute that makes sure the last
> > argument is a NULL).
>
> The first parameter to run_command_v_opt() must be a NULL terminated
> array of strings. argv.v[] after strvec_push*() is such a NULL
> terminated array, and is suitable to be passed to the function.
>
> That much human programmers would know.
>
> But does the compiler know that run_command_v_opt() requires a NULL
> terminated array of strings, and does it know to check that argv.v[]
> came from strvec_pushl() without any annotation in the first place?No, but I don't think that's the interesting part. If you're using strvec, it does the right thing, and it's hard to get it wrong. I'm more concerned about places where we manually write a list of strings, and it's easy to forget the trailing NULL.
In the existing code, that's done in the interface of strvec_pushl(), which will remind you if you write:
strvec_pushl(&arg, "checkout", start_head.buf, "--");
But after it is done in an initializer, which has no clue about the expected semantics. We only have to get strvec's invariants right once. But every ad-hoc command argv has to remember the trailing NULL.
> For such a check to happen, I think we need to tell the compiler > with some annotation that the first parameter to run_command_v_opt() > is supposed to be a NULL terminated char *[] array.
Right, but I would not expect the compiler to realize that strvec maintains the ends-in-NULL invariant. It would have to be quite a clever compiler.
In theory it could realize that argv is declared as an array locally, and could make sure it ends in NULL as a compile-time check.
So it would have to be: "check this statically if you can, but otherwise assume it's OK" kind of warning. But it's all kind of moot since I don't think any such annotation exists. :)
Possibly a linter like sparse could complain about declaring a variable called argv that doesn't end in NULL. I don't think it's worth anybody spending too much time on it, though. This hasn't historically been a big source of bugs.
Show 8 quoted lines
> > Probably not that big a deal in practice. It would be nice if there was > > a way to annotate this for the compiler, but I don't think there's any > > attribute for "this pointer-to-pointer parameter should have a NULL at > > the end". > > But the code before this patch is safe only for strvec_pushl() call, > not run_command_v_opt() call, so we are not losing anything, I would > think.
The bug I'm worried about it is in a human writing the list of strings and forgetting the NULL, so there we are losing the (admittedly minor) protection.
-Peff