]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev-rules: fix matching of token types that support alternative patterns
authorDmitry V. Levin <ldv@strace.io>
Thu, 16 Mar 2023 08:00:00 +0000 (08:00 +0000)
committerDmitry V. Levin <ldv@strace.io>
Sun, 19 Mar 2023 11:32:09 +0000 (11:32 +0000)
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")
src/shared/udev-util.c
src/test/test-udev-util.c

index a4b17be097929da24ee1566d737bff081d1eeca3..646eaf913549f6d580b86afe0dbc9c5a35c82e75 100644 (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;
index 1db2dad4ff9800c60296e2d9846158a8b0326c5e..4be3694e9ebb1cd3633c267f804a7021e0c835a3 100644 (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');
         }
 }