From: Dmitry V. Levin Date: Thu, 16 Mar 2023 08:00:00 +0000 (+0000) Subject: udev-rules: fix matching of token types that support alternative patterns X-Git-Tag: v254-rc1~980^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c43ff248f94266cfc93e300a2d3d163ed805e55b;p=thirdparty%2Fsystemd.git udev-rules: fix matching of token types that support alternative patterns For those token types that support matching of alternative patterns, their token values are interpreted as nulstr, so make sure the parser does the right thing and makes these token values terminated by two subsequent NULs so they could be safely interpreted as nulstr. Before this fix, the following rules would result to "echo foo" invocation: ENV{foo}=", RUN" ENV{foo}=="bar", RUN+="echo foo" because the value of `ENV{foo}` is treated as nulstr, and it used to match against alternative patterns, in this case `bar`, `, RUN`, and `="echo foo`. Fixes: 25de7aa7b90c ("udev: modernize udev-rules.c") --- diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index a4b17be0979..646eaf91354 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -347,11 +347,10 @@ int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) { str += is_escaped; if (str[0] != '"') return -EINVAL; - str++; if (!is_escaped) { /* unescape double quotation '\"'->'"' */ - for (i = j = str; *i != '"'; i++, j++) { + for (j = str, i = str + 1; *i != '"'; i++, j++) { if (*i == '\0') return -EINVAL; if (i[0] == '\\' && i[1] == '"') @@ -359,12 +358,17 @@ int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) { *j = *i; } j[0] = '\0'; + /* + * The return value must be terminated by two subsequent NULs + * so it could be safely interpreted as nulstr. + */ + j[1] = '\0'; } else { _cleanup_free_ char *unescaped = NULL; ssize_t l; /* find the end position of value */ - for (i = str; *i != '"'; i++) { + for (i = str + 1; *i != '"'; i++) { if (i[0] == '\\') i++; if (*i == '\0') @@ -372,12 +376,17 @@ int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) { } i[0] = '\0'; - l = cunescape_length(str, i - str, 0, &unescaped); + l = cunescape_length(str + 1, i - (str + 1), 0, &unescaped); if (l < 0) return l; - assert(l <= i - str); + assert(l <= i - (str + 1)); memcpy(str, unescaped, l + 1); + /* + * The return value must be terminated by two subsequent NULs + * so it could be safely interpreted as nulstr. + */ + str[l + 1] = '\0'; } *ret_value = str; diff --git a/src/test/test-udev-util.c b/src/test/test-udev-util.c index 1db2dad4ff9..4be3694e9eb 100644 --- a/src/test/test-udev-util.c +++ b/src/test/test-udev-util.c @@ -24,6 +24,11 @@ static void test_udev_rule_parse_value_one(const char *in, const char *expected_ } else { assert_se(streq_ptr(value, expected_value)); assert_se(endpos == str + strlen(in)); + /* + * The return value must be terminated by two subsequent NULs + * so it could be safely interpreted as nulstr. + */ + assert_se(value[strlen(value) + 1] == '\0'); } }