On Mon, Oct 10 2022, Junio C Hamano wrote: > Jeff King <peff@xxxxxxxx> writes: > >> On Fri, Oct 07, 2022 at 05:08:42PM +0200, René Scharfe wrote: >> >>> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c >>> index 501245fac9..28ef7ec2a4 100644 >>> --- a/builtin/bisect--helper.c >>> +++ b/builtin/bisect--helper.c >>> @@ -765,11 +765,10 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a >>> strbuf_read_file(&start_head, git_path_bisect_start(), 0); >>> strbuf_trim(&start_head); >>> if (!no_checkout) { >>> - 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? > > 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. > > In the code before the patch, strvec_pushl() is annotated to require > the last arg to be NULL, but without following data/control flow, it > may not be clear to the compiler that argv.v[] will be NULL terminated > after the function returns. If the compiler is sufficiently clever > to figure it out, with the knowledge that run_command_v_opt() must > be given a NULL terminated array of strings, we do have compiler > protection to make the run_command_v_opt() safe. > > But if the code tells the compiler that run_command_v_opt() must be > given a NULL terminated array of strings, it is obvious that the > array passed by the code after the patch, i.e. argv[], is NULL > terminated, and the compiler would be happy, as well. > >> 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. Yes, and if we suppose a bug like this sneaking in one way or the other: diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index 28ef7ec2a48..a7f9d43a6f1 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -766,7 +766,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a strbuf_trim(&start_head); if (!no_checkout) { const char *argv[] = { "checkout", start_head.buf, - "--", NULL }; + "--" }; if (run_command_v_opt(argv, RUN_GIT_CMD)) { res = error(_("checking out '%s' failed." I don't know a way to statically flag that, but we'll catch it with SANITIZE=address: + git bisect start 32a594a3fdac2d57cf6d02987e30eec68511498c 88bcdc1839f0ad191ffdd65cae2a2a862d682151 -- ================================================================= ==1734==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe6bbf5c18 at pc 0x55d31729bc7a bp 0x7ffe6bbf5920 sp 0x7ffe6bbf5918 READ of size 8 at 0x7ffe6bbf5c18 thread T0 #0 0x55d31729bc79 in strvec_pushv strvec.c:55 #1 0x55d317232be9 in run_command_v_opt_cd_env_tr2 run-command.c:1026 #2 0x55d317232a3c in run_command_v_opt_cd_env run-command.c:1019 #3 0x55d3172329d1 in run_command_v_opt run-command.c:1009 #4 0x55d316c1337a in bisect_start builtin/bisect--helper.c:771 #5 0x55d316c17d06 in cmd_bisect__helper builtin/bisect--helper.c:1346 #6 0x55d316bf190f in run_builtin git.c:466 #7 0x55d316bf22bb in handle_builtin git.c:721 #8 0x55d316bf29e5 in run_argv git.c:788 #9 0x55d316bf375f in cmd_main git.c:921 #10 0x55d316e79a06 in main common-main.c:57 #11 0x7fceab0a0209 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #12 0x7fceab0a02bb in __libc_start_main_impl ../csu/libc-start.c:389 #13 0x55d316bed210 in _start (git+0x1d7210) So I'm not worried about it happening in the first place, but if it does we'd also need to have no test coverage of the relevant code to miss it.