env-util: ensure NUL termination of the replace_env_argv() output array

The output array is allocated with new() and left uninitialized, but a
bare unset "$VAR" token expands to nothing and writes no terminator.
When such a token leads or is the only word, the returned strv is left
without a trailing NULL.

Follow-up for f331434d13
This commit is contained in:
Luca Boccassi
2026-06-30 11:49:53 +01:00
parent a3bf0f2f86
commit 1958539136
2 changed files with 26 additions and 0 deletions
+2
View File
@@ -948,6 +948,8 @@ int replace_env_argv(
}
k += q;
/* reallocarray() does not zero-initialize the grown tail, add NUL termination */
n[k] = NULL;
continue;
}
+24
View File
@@ -590,4 +590,28 @@ TEST(strv_env_get_merged) {
ASSERT_TRUE(strv_equal(m, expected));
}
TEST(replace_env_argv_unterminated) {
/* A bare unset "$VAR" token expands to nothing and advances neither the write index nor writes a
* terminator, so the output strv must still be NUL-terminated when such a token leads or is the
* only word. */
_cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL, **d = NULL;
/* Single bare unset variable: result must be a properly terminated empty strv. */
ASSERT_OK(replace_env_argv(STRV_MAKE("$THIS_IS_UNSET"), STRV_MAKE("FOO=BAR"), &a, NULL, NULL));
ASSERT_TRUE(strv_isempty(a));
/* Only unset variables. */
ASSERT_OK(replace_env_argv(STRV_MAKE("$THIS_IS_UNSET", "$ALSO_UNSET"), STRV_MAKE("FOO=BAR"), &b, NULL, NULL));
ASSERT_TRUE(strv_isempty(b));
/* Trailing bare unset variable after an expanded one. */
ASSERT_OK(replace_env_argv(STRV_MAKE("$FOO", "$THIS_IS_UNSET"), STRV_MAKE("FOO=BAR"), &c, NULL, NULL));
ASSERT_TRUE(strv_equal(c, STRV_MAKE("BAR")));
/* Trailing bare unset variable after a literal word. */
ASSERT_OK(replace_env_argv(STRV_MAKE("hello", "$THIS_IS_UNSET"), STRV_MAKE("FOO=BAR"), &d, NULL, NULL));
ASSERT_TRUE(strv_equal(d, STRV_MAKE("hello")));
}
DEFINE_TEST_MAIN(LOG_DEBUG);