Skip to content

Commit afb0653

Browse files
pks-tgitster
authored andcommitted
biultin/rev-parse: fix memory leaks in --parseopt mode
We have a bunch of memory leaks in git-rev-parse(1)'s `--parseopt` mode. Refactor the code to use `struct strvec`s to make it easier for us to track the lifecycle of those leaking variables and then free them. While at it, remove the unneeded static lifetime for some of the variables. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 11ee9a7 commit afb0653

File tree

3 files changed

+32
-23
lines changed

3 files changed

+32
-23
lines changed

builtin/rev-parse.c

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -423,12 +423,12 @@ static char *findspace(const char *s)
423423

424424
static int cmd_parseopt(int argc, const char **argv, const char *prefix)
425425
{
426-
static int keep_dashdash = 0, stop_at_non_option = 0;
427-
static char const * const parseopt_usage[] = {
426+
int keep_dashdash = 0, stop_at_non_option = 0;
427+
char const * const parseopt_usage[] = {
428428
N_("git rev-parse --parseopt [<options>] -- [<args>...]"),
429429
NULL
430430
};
431-
static struct option parseopt_opts[] = {
431+
struct option parseopt_opts[] = {
432432
OPT_BOOL(0, "keep-dashdash", &keep_dashdash,
433433
N_("keep the `--` passed as an arg")),
434434
OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option,
@@ -438,12 +438,11 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
438438
N_("output in stuck long form")),
439439
OPT_END(),
440440
};
441-
static const char * const flag_chars = "*=?!";
442-
443441
struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT;
444-
const char **usage = NULL;
442+
struct strvec longnames = STRVEC_INIT;
443+
struct strvec usage = STRVEC_INIT;
445444
struct option *opts = NULL;
446-
int onb = 0, osz = 0, unb = 0, usz = 0;
445+
size_t opts_nr = 0, opts_alloc = 0;
447446

448447
strbuf_addstr(&parsed, "set --");
449448
argc = parse_options(argc, argv, prefix, parseopt_opts, parseopt_usage,
@@ -453,16 +452,16 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
453452

454453
/* get the usage up to the first line with a -- on it */
455454
for (;;) {
455+
strbuf_reset(&sb);
456456
if (strbuf_getline(&sb, stdin) == EOF)
457457
die(_("premature end of input"));
458-
ALLOC_GROW(usage, unb + 1, usz);
459458
if (!strcmp("--", sb.buf)) {
460-
if (unb < 1)
459+
if (!usage.nr)
461460
die(_("no usage string given before the `--' separator"));
462-
usage[unb] = NULL;
463461
break;
464462
}
465-
usage[unb++] = strbuf_detach(&sb, NULL);
463+
464+
strvec_push(&usage, sb.buf);
466465
}
467466

468467
/* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */
@@ -474,10 +473,10 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
474473
if (!sb.len)
475474
continue;
476475

477-
ALLOC_GROW(opts, onb + 1, osz);
478-
memset(opts + onb, 0, sizeof(opts[onb]));
476+
ALLOC_GROW(opts, opts_nr + 1, opts_alloc);
477+
memset(opts + opts_nr, 0, sizeof(*opts));
479478

480-
o = &opts[onb++];
479+
o = &opts[opts_nr++];
481480
help = findspace(sb.buf);
482481
if (!help || sb.buf == help) {
483482
o->type = OPTION_GROUP;
@@ -494,20 +493,22 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
494493
o->callback = &parseopt_dump;
495494

496495
/* name(s) */
497-
s = strpbrk(sb.buf, flag_chars);
496+
s = strpbrk(sb.buf, "*=?!");
498497
if (!s)
499498
s = help;
500499

501500
if (s == sb.buf)
502501
die(_("missing opt-spec before option flags"));
503502

504-
if (s - sb.buf == 1) /* short option only */
503+
if (s - sb.buf == 1) { /* short option only */
505504
o->short_name = *sb.buf;
506-
else if (sb.buf[1] != ',') /* long option only */
507-
o->long_name = xmemdupz(sb.buf, s - sb.buf);
508-
else {
505+
} else if (sb.buf[1] != ',') { /* long option only */
506+
o->long_name = strvec_pushf(&longnames, "%.*s",
507+
(int)(s - sb.buf), sb.buf);
508+
} else {
509509
o->short_name = *sb.buf;
510-
o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2);
510+
o->long_name = strvec_pushf(&longnames, "%.*s",
511+
(int)(s - sb.buf - 2), sb.buf + 2);
511512
}
512513

513514
/* flags */
@@ -537,17 +538,23 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
537538
strbuf_release(&sb);
538539

539540
/* put an OPT_END() */
540-
ALLOC_GROW(opts, onb + 1, osz);
541-
memset(opts + onb, 0, sizeof(opts[onb]));
542-
argc = parse_options(argc, argv, prefix, opts, usage,
541+
ALLOC_GROW(opts, opts_nr + 1, opts_alloc);
542+
memset(opts + opts_nr, 0, sizeof(*opts));
543+
argc = parse_options(argc, argv, prefix, opts, usage.v,
543544
(keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0) |
544545
(stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) |
545546
PARSE_OPT_SHELL_EVAL);
546547

547548
strbuf_addstr(&parsed, " --");
548549
sq_quote_argv(&parsed, argv);
549550
puts(parsed.buf);
551+
550552
strbuf_release(&parsed);
553+
strbuf_release(&sb);
554+
strvec_clear(&longnames);
555+
strvec_clear(&usage);
556+
free((char *) opts->help);
557+
free(opts);
551558
return 0;
552559
}
553560

t/t5150-request-pull.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ test_description='Test workflows involving pull request.'
55
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
66
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
77

8+
TEST_PASSES_SANITIZE_LEAK=true
89
. ./test-lib.sh
910

1011
if ! test_have_prereq PERL

t/t7006-pager.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
test_description='Test automatic use of a pager.'
44

5+
TEST_PASSES_SANITIZE_LEAK=true
56
. ./test-lib.sh
67
. "$TEST_DIRECTORY"/lib-pager.sh
78
. "$TEST_DIRECTORY"/lib-terminal.sh

0 commit comments

Comments
 (0)