Subject: Re: [PATCH v5 18/21] commit-graph: use string-list API for input

From: Ævar Arnfjörð Bjarmason <[email-masked]>
Date: Wed, 06 Jun 2018 14:26:33 +0200

On Wed, Jun 06 2018, Derrick Stolee wrote:

> On 6/6/2018 8:11 AM, Ævar Arnfjörð Bjarmason wrote:
>> On Wed, Jun 06 2018, Derrick Stolee wrote:
>>
>>> Signed-off-by: Derrick Stolee <[email-masked]>
>>> ---
>>>   builtin/commit-graph.c | 39 +++++++++++++--------------------------
>>>   commit-graph.c         | 15 +++++++--------
>>>   commit-graph.h         |  7 +++----
>>>   3 files changed, 23 insertions(+), 38 deletions(-)
>>> diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
>>> index 3079cde6f9..d8eb8278b3 100644
>>> --- a/builtin/commit-graph.c
>>> +++ b/builtin/commit-graph.c
>>> @@ -118,13 +118,9 @@ static int graph_read(int argc, const char **argv)
>>>
>>>   static int graph_write(int argc, const char **argv)
>>>   {
>>> -	const char **pack_indexes = NULL;
>>> -	int packs_nr = 0;
>>> -	const char **commit_hex = NULL;
>>> -	int commits_nr = 0;
>>> -	const char **lines = NULL;
>>> -	int lines_nr = 0;
>>> -	int lines_alloc = 0;
>>> +	struct string_list *pack_indexes = NULL;
>>> +	struct string_list *commit_hex = NULL;
>>> +	struct string_list lines;
>>>
>>>   	static struct option builtin_commit_graph_write_options[] = {
>>>   		OPT_STRING(0, "object-dir", &opts.obj_dir,
>>> @@ -150,32 +146,23 @@ static int graph_write(int argc, const char **argv)
>>>
>>>   	if (opts.stdin_packs || opts.stdin_commits) {
>>>   		struct strbuf buf = STRBUF_INIT;
>>> -		lines_nr = 0;
>>> -		lines_alloc = 128;
>>> -		ALLOC_ARRAY(lines, lines_alloc);
>>> -
>>> -		while (strbuf_getline(&buf, stdin) != EOF) {
>>> -			ALLOC_GROW(lines, lines_nr + 1, lines_alloc);
>>> -			lines[lines_nr++] = strbuf_detach(&buf, NULL);
>>> -		}
>>> -
>>> -		if (opts.stdin_packs) {
>>> -			pack_indexes = lines;
>>> -			packs_nr = lines_nr;
>>> -		}
>>> -		if (opts.stdin_commits) {
>>> -			commit_hex = lines;
>>> -			commits_nr = lines_nr;
>>> -		}
>>> +		string_list_init(&lines, 0);
>>> +
>>> +		while (strbuf_getline(&buf, stdin) != EOF)
>>> +			string_list_append(&lines, strbuf_detach(&buf, NULL));
>>> +
>>> +		if (opts.stdin_packs)
>>> +			pack_indexes = &lines;
>>> +		if (opts.stdin_commits)
>>> +			commit_hex = &lines;
>>>   	}
>>>
>>>   	write_commit_graph(opts.obj_dir,
>>>   			   pack_indexes,
>>> -			   packs_nr,
>>>   			   commit_hex,
>>> -			   commits_nr,
>>>   			   opts.append);
>>>
>>> +	string_list_clear(&lines, 0);
>>>   	return 0;
>>>   }
>> This results in an invalid free() & segfault because you're freeing
>> &lines which may not have been allocated by string_list_init().
>
> Good point. Did my tests not catch this? (seems it requires calling
> `git commit-graph write` with no `--stdin-packs` or
> `--stdin-commits`).

Most of your tests (t5318-commit-graph.sh) segfaulted, but presumably
you're on a more forgiving compiler/platform/options. I compiled with
-O0 -g on clang 4.0.1-8 + Debian testing.

>>
>> Monkeypatch on top which I used to fix it:
>>
>>      diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
>>      index 76423b3fa5..c7eb68aa3a 100644
>>      --- a/builtin/commit-graph.c
>>      +++ b/builtin/commit-graph.c
>>      @@ -122,6 +122,7 @@ static int graph_write(int argc, const char **argv)
>>              struct string_list *pack_indexes = NULL;
>>              struct string_list *commit_hex = NULL;
>>              struct string_list lines;
>>      +       int free_lines = 0;
>>
>>              static struct option builtin_commit_graph_write_options[] = {
>>                      OPT_STRING(0, "object-dir", &opts.obj_dir,
>>      @@ -155,6 +156,7 @@ static int graph_write(int argc, const char **argv)
>>              if (opts.stdin_packs || opts.stdin_commits) {
>>                      struct strbuf buf = STRBUF_INIT;
>>                      string_list_init(&lines, 0);
>>      +               free_lines = 1;
>>
>>                      while (strbuf_getline(&buf, stdin) != EOF)
>>                              string_list_append(&lines, strbuf_detach(&buf, NULL));
>>      @@ -170,7 +172,8 @@ static int graph_write(int argc, const char **argv)
>>                                 commit_hex,
>>                                 opts.append);
>>
>>      -       string_list_clear(&lines, 0);
>>      +       if (free_lines)
>>      +               string_list_clear(&lines, 0);
>>              return 0;
>>       }
>>
>> But probably having a pointer to the struct which is NULL etc. is
>> better.
>
> Wouldn't the easiest fix be to call `string_list_init(&lines, 0)`
> outside of any conditional?

Sure that works too. We'd be doing the init when we don't need it, but
it's not like this part is performance critical or anything...