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: 25de7aa7b9 ("udev: modernize udev-rules.c")
This commit is contained in:
Dmitry V. Levin 2023-03-16 08:00:00 +00:00
parent 432f1fa8c7
commit c43ff248f9
2 changed files with 19 additions and 5 deletions

View file

@ -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;

View file

@ -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');
}
}