]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev-rules: fix check for conflicting and duplicate expressions 26886/head
authorDmitry V. Levin <ldv@strace.io>
Fri, 17 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)
Fix check for conflicting and duplicate expressions of types that
support alternative patterns.

Fixes: 3ec58d0cd8f6 ("udev-rules: check for conflicting and duplicate expressions")
src/udev/udev-rules.c
test/units/testsuite-17.11.sh

index a1c17a24e8c639e4cf3c6e98314a85480ae974fe..f708f79c9af254fb5e338513f2336dc4887abd95 100644 (file)
@@ -477,6 +477,10 @@ static UdevRuleSubstituteType rule_get_substitution_type(const char *str) {
         return SUBST_TYPE_PLAIN;
 }
 
+static bool type_has_nulstr_value(UdevRuleTokenType type) {
+        return type < TK_M_TEST || type == TK_M_RESULT;
+}
+
 static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type, UdevRuleOperatorType op, char *value, void *data) {
         _cleanup_(udev_rule_token_freep) UdevRuleToken *token = NULL;
         UdevRuleMatchType match_type = _MATCH_TYPE_INVALID;
@@ -505,7 +509,7 @@ static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type,
                 else
                         match_type = MATCH_TYPE_PLAIN;
 
-                if (type < TK_M_TEST || type == TK_M_RESULT) {
+                if (type_has_nulstr_value(type)) {
                         /* Convert value string to nulstr. */
                         bool bar = true, empty = false;
                         char *a, *b;
@@ -1361,18 +1365,32 @@ static bool token_type_and_data_eq(const UdevRuleToken *a, const UdevRuleToken *
                (token_data_is_string(a->type) ? streq_ptr(a->data, b->data) : (a->data == b->data));
 }
 
-static bool token_value_eq(const UdevRuleToken *a, const UdevRuleToken *b) {
+static bool nulstr_eq(const char *a, const char *b) {
+        NULSTR_FOREACH(i, a)
+                if (!nulstr_contains(b, i))
+                        return false;
+
+        NULSTR_FOREACH(i, b)
+                if (!nulstr_contains(a, i))
+                        return false;
+
+        return true;
+}
+
+static bool token_type_and_value_eq(const UdevRuleToken *a, const UdevRuleToken *b) {
         assert(a);
         assert(b);
 
-        if (a->match_type != b->match_type)
+        if (a->type != b->type ||
+            a->match_type != b->match_type)
                 return false;
 
         /* token value is ignored for certain match types */
         if (IN_SET(a->match_type, MATCH_TYPE_EMPTY, MATCH_TYPE_SUBSYSTEM))
                 return true;
 
-        return streq_ptr(a->value, b->value);
+        return type_has_nulstr_value(a->type) ? nulstr_eq(a->value, b->value) :
+                                                streq_ptr(a->value, b->value);
 }
 
 static bool conflicting_op(UdevRuleOperatorType a, UdevRuleOperatorType b) {
@@ -1387,7 +1405,7 @@ static bool tokens_eq(const UdevRuleToken *a, const UdevRuleToken *b) {
 
         return a->attr_subst_type == b->attr_subst_type &&
                a->attr_match_remove_trailing_whitespace == b->attr_match_remove_trailing_whitespace &&
-               token_value_eq(a, b) &&
+               token_type_and_value_eq(a, b) &&
                token_type_and_data_eq(a, b);
 }
 
index 50e18209576ec556b0e07c887e5fd18a393007fc..5f067f02b299afda5a1608b2f10c6e15b2f7a1c8 100755 (executable)
@@ -277,14 +277,22 @@ test_syntax_error 'LABEL="b"' 'LABEL="b" is unused.'
 test_syntax_error 'a="b"' "Invalid key 'a'"
 test_syntax_error 'KERNEL=="", KERNEL=="?*", NAME="a"' 'conflicting match expressions, the line takes no effect'
 test_syntax_error 'KERNEL=="abc", KERNEL!="abc", NAME="b"' 'conflicting match expressions, the line takes no effect'
+test_syntax_error 'KERNEL=="|a|b", KERNEL!="b|a|", NAME="c"' 'conflicting match expressions, the line takes no effect'
 # shellcheck disable=SC2016
 test_syntax_error 'ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{DISKSEQ}!="?*" ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"' \
                   'conflicting match expressions, the line takes no effect'
 test_syntax_error 'KERNEL!="", KERNEL=="?*", NAME="a"' 'duplicate expressions'
+test_syntax_error 'KERNEL=="|a|b", KERNEL=="b|a|", NAME="c"' 'duplicate expressions'
 # shellcheck disable=SC2016
 test_syntax_error 'ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{DISKSEQ}=="?*" ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"' \
                   'duplicate expressions'
 
+cat >"${rules}" <<'EOF'
+KERNEL=="a|b", KERNEL=="a|c", NAME="d"
+KERNEL=="a|b", KERNEL!="a|c", NAME="d"
+EOF
+assert_0 "${rules}"
+
 echo 'GOTO="a"' >"${rules}"
 cat >"${exp}" <<EOF
 ${rules}:1 GOTO="a" has no matching label, ignoring