Skip to content

Commit 9e2d884

Browse files
avargitster
authored andcommitted
config API: add "string" version of *_value_multi(), fix segfaults
Fix numerous and mostly long-standing segfaults in consumers of the *_config_*value_multi() API. As discussed in the preceding commit an empty key in the config syntax yields a "NULL" string, which these users would give to strcmp() (or similar), resulting in segfaults. As this change shows, most users users of the *_config_*value_multi() API didn't really want such an an unsafe and low-level API, let's give them something with the safety of git_config_get_string() instead. This fix is similar to what the *_string() functions and others acquired in[1] and [2]. Namely introducing and using a safer "*_get_string_multi()" variant of the low-level "_*value_multi()" function. This fixes segfaults in code introduced in: - d811c8e (versionsort: support reorder prerelease suffixes, 2015-02-26) - c026557 (versioncmp: generalize version sort suffix reordering, 2016-12-08) - a086f92 (submodule: decouple url and submodule interest, 2017-03-17) - a6be5e6 (log: add log.excludeDecoration config option, 2020-04-16) - 9215629 (log: add default decoration filter, 2022-08-05) - 50a044f (gc: replace config subprocesses with API calls, 2022-09-27) There are now two users ofthe low-level API: - One in "builtin/for-each-repo.c", which we'll convert in a subsequent commit. - The "t/helper/test-config.c" code added in [3]. As seen in the preceding commit we need to give the "t/helper/test-config.c" caller these "NULL" entries. We could also alter the underlying git_configset_get_value_multi() function to be "string safe", but doing so would leave no room for other variants of "*_get_value_multi()" that coerce to other types. Such coercion can't be built on the string version, since as we've established "NULL" is a true value in the boolean context, but if we coerced it to "" for use in a list of strings it'll be subsequently coerced to "false" as a boolean. The callback pattern being used here will make it easy to introduce e.g. a "multi" variant which coerces its values to "bool", "int", "path" etc. 1. 40ea4ed (Add config_error_nonbool() helper function, 2008-02-11) 2. 6c47d0e (config.c: guard config parser from value=NULL, 2008-02-11). 3. 4c715eb (test-config: add tests for the config_set API, 2014-07-28) Signed-off-by: Ævar Arnfjörð Bjarmason <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1c7e239 commit 9e2d884

12 files changed

+105
-22
lines changed

builtin/gc.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,7 +1510,7 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
15101510
if (git_config_get("maintenance.strategy"))
15111511
git_config_set("maintenance.strategy", "incremental");
15121512

1513-
if (!git_config_get_value_multi(key, &list)) {
1513+
if (!git_config_get_string_multi(key, &list)) {
15141514
for_each_string_list_item(item, list) {
15151515
if (!strcmp(maintpath, item->string)) {
15161516
found = 1;
@@ -1578,8 +1578,8 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
15781578
git_configset_add_file(&cs, config_file);
15791579
}
15801580
if (!(config_file
1581-
? git_configset_get_value_multi(&cs, key, &list)
1582-
: git_config_get_value_multi(key, &list))) {
1581+
? git_configset_get_string_multi(&cs, key, &list)
1582+
: git_config_get_string_multi(key, &list))) {
15831583
for_each_string_list_item(item, list) {
15841584
if (!strcmp(maintpath, item->string)) {
15851585
found = 1;

builtin/log.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ static void set_default_decoration_filter(struct decoration_filter *decoration_f
184184
struct string_list *include = decoration_filter->include_ref_pattern;
185185
const struct string_list *config_exclude;
186186

187-
if (!git_config_get_value_multi("log.excludeDecoration",
188-
&config_exclude)) {
187+
if (!git_config_get_string_multi("log.excludeDecoration",
188+
&config_exclude)) {
189189
struct string_list_item *item;
190190
for_each_string_list_item(item, config_exclude)
191191
string_list_append(decoration_filter->exclude_ref_config_pattern,

config.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2434,6 +2434,25 @@ int git_configset_get_value_multi(struct config_set *cs, const char *key,
24342434
return 0;
24352435
}
24362436

2437+
static int check_multi_string(struct string_list_item *item, void *util)
2438+
{
2439+
return item->string ? 0 : config_error_nonbool(util);
2440+
}
2441+
2442+
int git_configset_get_string_multi(struct config_set *cs, const char *key,
2443+
const struct string_list **dest)
2444+
{
2445+
int ret;
2446+
2447+
if ((ret = git_configset_get_value_multi(cs, key, dest)))
2448+
return ret;
2449+
if ((ret = for_each_string_list((struct string_list *)*dest,
2450+
check_multi_string, (void *)key)))
2451+
return ret;
2452+
2453+
return 0;
2454+
}
2455+
24372456
int git_configset_get(struct config_set *cs, const char *key)
24382457
{
24392458
struct config_set_element *e;
@@ -2602,6 +2621,13 @@ int repo_config_get_value_multi(struct repository *repo, const char *key,
26022621
return git_configset_get_value_multi(repo->config, key, dest);
26032622
}
26042623

2624+
int repo_config_get_string_multi(struct repository *repo, const char *key,
2625+
const struct string_list **dest)
2626+
{
2627+
git_config_check_init(repo);
2628+
return git_configset_get_string_multi(repo->config, key, dest);
2629+
}
2630+
26052631
int repo_config_get_string(struct repository *repo,
26062632
const char *key, char **dest)
26072633
{
@@ -2717,6 +2743,12 @@ int git_config_get_value_multi(const char *key, const struct string_list **dest)
27172743
return repo_config_get_value_multi(the_repository, key, dest);
27182744
}
27192745

2746+
int git_config_get_string_multi(const char *key,
2747+
const struct string_list **dest)
2748+
{
2749+
return repo_config_get_string_multi(the_repository, key, dest);
2750+
}
2751+
27202752
int git_config_get_string(const char *key, char **dest)
27212753
{
27222754
return repo_config_get_string(the_repository, key, dest);

config.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,19 @@ RESULT_MUST_BE_USED
472472
int git_configset_get_value_multi(struct config_set *cs, const char *key,
473473
const struct string_list **dest);
474474

475+
/**
476+
* A validation wrapper for git_configset_get_value_multi() which does
477+
* for it what git_configset_get_string() does for
478+
* git_configset_get_value().
479+
*
480+
* The configuration syntax allows for "[section] key", which will
481+
* give us a NULL entry in the "struct string_list", as opposed to
482+
* "[section] key =" which is the empty string. Most users of the API
483+
* are not prepared to handle NULL in a "struct string_list".
484+
*/
485+
int git_configset_get_string_multi(struct config_set *cs, const char *key,
486+
const struct string_list **dest);
487+
475488
/**
476489
* Clears `config_set` structure, removes all saved variable-value pairs.
477490
*/
@@ -522,6 +535,9 @@ int repo_config_get_value(struct repository *repo,
522535
RESULT_MUST_BE_USED
523536
int repo_config_get_value_multi(struct repository *repo, const char *key,
524537
const struct string_list **dest);
538+
RESULT_MUST_BE_USED
539+
int repo_config_get_string_multi(struct repository *repo, const char *key,
540+
const struct string_list **dest);
525541
int repo_config_get_string(struct repository *repo,
526542
const char *key, char **dest);
527543
int repo_config_get_string_tmp(struct repository *repo,
@@ -583,6 +599,9 @@ int git_config_get_value(const char *key, const char **value);
583599
RESULT_MUST_BE_USED
584600
int git_config_get_value_multi(const char *key,
585601
const struct string_list **dest);
602+
RESULT_MUST_BE_USED
603+
int git_config_get_string_multi(const char *key,
604+
const struct string_list **dest);
586605

587606
/**
588607
* Resets and invalidates the config cache.

pack-bitmap.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2303,7 +2303,7 @@ const struct string_list *bitmap_preferred_tips(struct repository *r)
23032303
{
23042304
const struct string_list *dest;
23052305

2306-
if (!repo_config_get_value_multi(r, "pack.preferbitmaptips", &dest))
2306+
if (!repo_config_get_string_multi(r, "pack.preferbitmaptips", &dest))
23072307
return dest;
23082308
return NULL;
23092309
}

submodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ int is_tree_submodule_active(struct repository *repo,
274274
free(key);
275275

276276
/* submodule.active is set */
277-
if (!repo_config_get_value_multi(repo, "submodule.active", &sl)) {
277+
if (!repo_config_get_string_multi(repo, "submodule.active", &sl)) {
278278
struct pathspec ps;
279279
struct strvec args = STRVEC_INIT;
280280
const struct string_list_item *item;

t/t4202-log.sh

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -835,15 +835,19 @@ test_expect_success 'log.decorate configuration' '
835835
836836
'
837837

838-
test_expect_failure 'parse log.excludeDecoration with no value' '
838+
test_expect_success 'parse log.excludeDecoration with no value' '
839839
cp .git/config .git/config.orig &&
840840
test_when_finished mv .git/config.orig .git/config &&
841841
842842
cat >>.git/config <<-\EOF &&
843843
[log]
844844
excludeDecoration
845845
EOF
846-
git log --decorate=short
846+
cat >expect <<-\EOF &&
847+
error: missing value for '\''log.excludeDecoration'\''
848+
EOF
849+
git log --decorate=short 2>actual &&
850+
test_cmp expect actual
847851
'
848852

849853
test_expect_success 'decorate-refs with glob' '

t/t5310-pack-bitmaps.sh

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ test_bitmap_cases () {
404404
)
405405
'
406406

407-
test_expect_failure 'pack.preferBitmapTips' '
407+
test_expect_success 'pack.preferBitmapTips' '
408408
git init repo &&
409409
test_when_finished "rm -rf repo" &&
410410
(
@@ -416,7 +416,11 @@ test_bitmap_cases () {
416416
[pack]
417417
preferBitmapTips
418418
EOF
419-
git repack -adb
419+
cat >expect <<-\EOF &&
420+
error: missing value for '\''pack.preferbitmaptips'\''
421+
EOF
422+
git repack -adb 2>actual &&
423+
test_cmp expect actual
420424
)
421425
'
422426

t/t7004-tag.sh

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1843,7 +1843,7 @@ test_expect_success 'invalid sort parameter in configuratoin' '
18431843
test_must_fail git tag -l "foo*"
18441844
'
18451845

1846-
test_expect_failure 'version sort handles empty value for versionsort.{prereleaseSuffix,suffix}' '
1846+
test_expect_success 'version sort handles empty value for versionsort.{prereleaseSuffix,suffix}' '
18471847
cp .git/config .git/config.orig &&
18481848
test_when_finished mv .git/config.orig .git/config &&
18491849
@@ -1852,7 +1852,12 @@ test_expect_failure 'version sort handles empty value for versionsort.{prereleas
18521852
prereleaseSuffix
18531853
suffix
18541854
EOF
1855-
git tag -l --sort=version:refname
1855+
cat >expect <<-\EOF &&
1856+
error: missing value for '\''versionsort.suffix'\''
1857+
error: missing value for '\''versionsort.prereleasesuffix'\''
1858+
EOF
1859+
git tag -l --sort=version:refname 2>actual &&
1860+
test_cmp expect actual
18561861
'
18571862

18581863
test_expect_success 'version sort with prerelease reordering' '

t/t7413-submodule-is-active.sh

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ test_expect_success 'is-active works with submodule.<name>.active config' '
5151
test-tool -C super submodule is-active sub1
5252
'
5353

54-
test_expect_failure 'is-active handles submodule.active config missing a value' '
54+
test_expect_success 'is-active handles submodule.active config missing a value' '
5555
cp super/.git/config super/.git/config.orig &&
5656
test_when_finished mv super/.git/config.orig super/.git/config &&
5757
@@ -60,7 +60,11 @@ test_expect_failure 'is-active handles submodule.active config missing a value'
6060
active
6161
EOF
6262
63-
test-tool -C super submodule is-active sub1
63+
cat >expect <<-\EOF &&
64+
error: missing value for '\''submodule.active'\''
65+
EOF
66+
test-tool -C super submodule is-active sub1 2>actual &&
67+
test_cmp expect actual
6468
'
6569

6670
test_expect_success 'is-active works with basic submodule.active config' '

0 commit comments

Comments
 (0)