From 11ba0a30f65fd76c9048183fce55df2677ecb29b Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Fri, 10 Jul 2026 16:04:31 +0800 Subject: [PATCH 1/5] udev-rules: accept cvm CONST matches CONST{cvm} is documented and already handled when udev rules are executed, but the parser rejected it before the rule could be used. Include cvm in the supported CONST key list. Follow-up for 6e2e83b48734e86992cbdbb329c48cc066cf7c96. Reproducer: $ printf 'CONST{cvm}=="none", NAME="x"\n' > /tmp/codex-cvm.rules $ udevadm verify /tmp/codex-cvm.rules /tmp/codex-cvm.rules:1 Invalid attribute for CONST. /tmp/codex-cvm.rules: udev rules check failed. 1 udev rules files have been checked. Success: 0 Fail: 1 --- src/udev/udev-rules.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index b96ba0fe33..42f6fde8a0 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -849,7 +849,7 @@ static int parse_token( } else r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr, is_case_insensitive, token_str); } else if (streq(key, "CONST")) { - if (isempty(attr) || !STR_IN_SET(attr, "arch", "virt")) + if (isempty(attr) || !STR_IN_SET(attr, "arch", "virt", "cvm")) return log_line_invalid_attr(rule_line, key); if (!is_match) return log_line_invalid_op(rule_line, key); From 13d8955088c00ca87619a146a1a85c3f67e4211d Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Fri, 10 Jul 2026 16:05:07 +0800 Subject: [PATCH 2/5] udevadm-hwdb: honor root when querying The deprecated udevadm hwdb command parsed --root for both update and test modes, but the test path always queried the host database. Pass the configured root to hwdb_query() so --root affects --test in the same way as systemd-hwdb query. Reproducer: $ tmp=$(mktemp -d) $ mkdir -p "$tmp/etc/udev/hwdb.d" $ printf 'test:codex-root-test\n ID_TEST_CODEX=from-root\n' > "$tmp/etc/udev/hwdb.d/99-codex.hwdb" $ systemd-hwdb --root="$tmp" update $ systemd-hwdb --root="$tmp" query test:codex-root-test ID_TEST_CODEX=from-root $ udevadm hwdb --root="$tmp" --test=test:codex-root-test udevadm hwdb is deprecated. Use systemd-hwdb instead. The last command was expected to print ID_TEST_CODEX=from-root, but queried the host database instead. --- src/udev/udevadm-hwdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c index bb6f03d540..0a38e6ee06 100644 --- a/src/udev/udevadm-hwdb.c +++ b/src/udev/udevadm-hwdb.c @@ -99,7 +99,7 @@ int verb_hwdb_main(int argc, char *argv[], uintptr_t _data, void *userdata) { } if (arg_test) - return hwdb_query(arg_test, NULL); + return hwdb_query(arg_test, arg_root); return 0; } From 9095da896fc1bdd86b5b749d71bd2aebc585e236 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Fri, 10 Jul 2026 16:05:24 +0800 Subject: [PATCH 3/5] udev-rules: drop truncated import output line When IMPORT{program} output was truncated, the code intended to drop the last incomplete result line before importing properties. It edited the command buffer instead of the output buffer, so the incomplete line could still be parsed and imported. Trim the result buffer and cover the behavior with the existing IMPORT test. Follow-up for 6b6e471a325bf149839c5c822b4ae3e66cb1d9a3. Reproducer: Configure an IMPORT{program} rule whose output exceeds UDEV_LINE_SIZE and ends with an incomplete property line, for example: TRUNCATED_OK=yes TRUNCATED_BAD= Only TRUNCATED_OK should be imported. Before this fix, the truncation handler edited the command buffer instead of the output buffer, so TRUNCATED_BAD could still be parsed from the truncated result. --- src/udev/udev-rules.c | 4 ++-- test/units/TEST-17-UDEV.IMPORT.sh | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 42f6fde8a0..88f633138e 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -2482,14 +2482,14 @@ static int udev_rule_apply_token_to_event( /* Drop the last line. */ bool found = false; - for (char *p = PTR_SUB1(buf + strlen(buf), buf); p; p = PTR_SUB1(p, buf)) + for (char *p = PTR_SUB1(result + strlen(result), result); p; p = PTR_SUB1(p, result)) if (strchr(NEWLINE, *p)) { *p = '\0'; found = true; break; } if (!found) - buf[0] = '\0'; + result[0] = '\0'; } r = strv_split_newlines_full(&lines, result, EXTRACT_RETAIN_ESCAPE); diff --git a/test/units/TEST-17-UDEV.IMPORT.sh b/test/units/TEST-17-UDEV.IMPORT.sh index f09b171066..f2f9a4100e 100755 --- a/test/units/TEST-17-UDEV.IMPORT.sh +++ b/test/units/TEST-17-UDEV.IMPORT.sh @@ -17,6 +17,17 @@ test -f /run/udev/data/c1:3 udevadm info /dev/null | grep 'E: HOGE=aa\\x20\\x20\\x20bb' >/dev/null udevadm info /dev/null | grep 'E: FOO=\\x20aaa\\x20' >/dev/null +cat >/run/udev/rules.d/50-testsuite.rules <<'EOF' +SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug" +ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", IMPORT{program}="/bin/sh -c 'printf \"TRUNCATED_OK=yes\nTRUNCATED_BAD=\"; i=0; while [ \"$i\" -lt 20000 ]; do printf A; i=$((i + 1)); done'" +EOF + +udevadm control --reload +SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null + +udevadm info /dev/null | grep 'E: TRUNCATED_OK=yes' >/dev/null +(! udevadm info /dev/null | grep 'E: TRUNCATED_BAD=' >/dev/null) + rm /run/udev/rules.d/50-testsuite.rules udevadm control --reload From 351e29e73971c061c0423eb5769a893367a7dcd5 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Fri, 10 Jul 2026 16:08:07 +0800 Subject: [PATCH 4/5] udevadm-control: reject oversized children-max The --children-max= option parsed into an unsigned value and then stored it in an int sentinel field. Values above INT_MAX could wrap negative and make the command look as if no control option had been specified. Reject oversized values before assigning the parsed value. Reproducer: $ udevadm control --children-max=2147483648 No control command option is specified. $ echo $? 1 The value should be rejected as out of range instead of being wrapped into the unset sentinel value. --- src/udev/udevadm-control.c | 3 +++ test/units/TEST-17-UDEV.sanity-check.sh | 1 + 2 files changed, 4 insertions(+) diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c index 0a0bb35fb5..cef3ec7fd5 100644 --- a/src/udev/udevadm-control.c +++ b/src/udev/udevadm-control.c @@ -125,6 +125,9 @@ static int parse_argv(int argc, char *argv[]) { r = safe_atou(opts.arg, &i); if (r < 0) return log_error_errno(r, "Failed to parse maximum number of children '%s': %m", opts.arg); + if (i > INT_MAX) + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), + "Maximum number of children is too large: %s", opts.arg); arg_max_children = i; break; } diff --git a/test/units/TEST-17-UDEV.sanity-check.sh b/test/units/TEST-17-UDEV.sanity-check.sh index 6246638fc9..18326c03ad 100755 --- a/test/units/TEST-17-UDEV.sanity-check.sh +++ b/test/units/TEST-17-UDEV.sanity-check.sh @@ -55,6 +55,7 @@ udevadm control -l notice udevadm control --log-level info udevadm control --log-level debug (! udevadm control -l hello) +(! udevadm control -m 2147483648) # Check if processing queued events has been stopped. udevadm control --stop-exec-queue From 4b7f3de68cde0ed9c8950374d345141e06721c0d Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Fri, 10 Jul 2026 16:09:34 +0800 Subject: [PATCH 5/5] udevadm-info: handle missing data db cleanup udevadm info --cleanup-db cleaned links and tags by comparing them against /run/udev/data, but asserted when that data directory was missing. Treat a missing data directory as an empty database, so stale link and tag entries can be removed without aborting. Add a database test that runs cleanup-db with links and tags present but no data directory. Reproducer: $ sudo unshare --mount --propagation private sh -c ' mount -t tmpfs tmpfs /run/udev mkdir -p /run/udev/links/foo udevadm info --cleanup-db ' Assertion 'datadir' failed at src/udev/udevadm-info.c:676, function cleanup_dirs_after_db_cleanup(). Aborting. Aborted (core dumped) The command should handle a missing /run/udev/data directory instead of aborting. --- src/udev/udevadm-info.c | 5 ++--- test/units/TEST-17-UDEV.database.sh | 11 +++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c index f2944b02f3..78c9520017 100644 --- a/src/udev/udevadm-info.c +++ b/src/udev/udevadm-info.c @@ -655,16 +655,16 @@ static void cleanup_dir(DIR *dir, mode_t mask, int depth) { * entries for devices in /run/udev/data (such as "b8:16"), and removes * all files except those that haven't been deleted in /run/udev/data * (i.e. they were skipped during db cleanup because of the db_persist flag). + * If the database directory does not exist, all entries are considered stale. */ static void cleanup_dir_after_db_cleanup(DIR *dir, DIR *datadir) { assert(dir); - assert(datadir); FOREACH_DIRENT_ALL(dent, dir, break) { if (dot_or_dot_dot(dent->d_name)) continue; - if (faccessat(dirfd(datadir), dent->d_name, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) + if (datadir && faccessat(dirfd(datadir), dent->d_name, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) /* The corresponding udev database file still exists. * Assuming the persistent flag is set for the database. */ continue; @@ -675,7 +675,6 @@ static void cleanup_dir_after_db_cleanup(DIR *dir, DIR *datadir) { static void cleanup_dirs_after_db_cleanup(DIR *dir, DIR *datadir) { assert(dir); - assert(datadir); FOREACH_DIRENT_ALL(dent, dir, break) { struct stat stats; diff --git a/test/units/TEST-17-UDEV.database.sh b/test/units/TEST-17-UDEV.database.sh index 56a79a665b..ed50daa5ad 100755 --- a/test/units/TEST-17-UDEV.database.sh +++ b/test/units/TEST-17-UDEV.database.sh @@ -15,4 +15,15 @@ udevadm wait --timeout=10 --removed --settle "/sys/class/net/$IFNAME" # CHeck if the database file is removed. [[ ! -e "/run/udev/data/n$IFINDEX" ]] +unshare --mount --propagation private bash -eux <<'EOF' +mount -t tmpfs tmpfs /run/udev +mkdir -p /run/udev/links/test /run/udev/tags/test +touch /run/udev/links/test/stale /run/udev/tags/test/stale + +udevadm info --cleanup-db + +[[ ! -e /run/udev/links/test ]] +[[ ! -e /run/udev/tags/test ]] +EOF + exit 0