Re: [PATCH v2] bisect--helper: plug strvec leak

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux