From 9725124635dbc061768ee61760c5a92c763cab91 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Mon, 13 Jul 2026 10:30:25 +0800 Subject: [PATCH 1/7] udev: avoid reading before empty capability masks When input_id prints a decoded capability bitmap, an all-zero bitmap can decrement the index to zero. Check the index before reading bitmask[val - 1]. Reproducer: udevadm test-builtin input_id with an all-zero capability mask Follow-up: 912541b0246ef315d4d851237483b98c9dd3f992 --- src/udev/udev-builtin-input_id.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c index ec232f3e93..532e3ed60b 100644 --- a/src/udev/udev-builtin-input_id.c +++ b/src/udev/udev-builtin-input_id.c @@ -118,7 +118,7 @@ static void get_cap_mask( val = bitmask_size / sizeof (unsigned long); /* skip trailing zeros */ - while (bitmask[val-1] == 0 && val > 0) + while (val > 0 && bitmask[val-1] == 0) --val; /* IN_SET() cannot be used in assert_cc(). */ From c5a6425b8149ff0957e93ea8396a071c675cbf7f Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Mon, 13 Jul 2026 10:33:34 +0800 Subject: [PATCH 2/7] udevadm-info: allow valueless attr filters The --attr-match= and --attr-nomatch= filters are documented as FILE[=VALUE], like the trigger filters. Accept a bare attribute name for sysfs attribute presence matches. Reproducer: udevadm info --export-db --attr-match=ifindex Before: Expected = instead of 'ifindex' Follow-up: a6b4b2fa010f6dc5e18f1a14d93204c6c1416278 --- src/udev/udevadm-info.c | 12 ++---------- test/units/TEST-17-UDEV.sanity-check.sh | 2 ++ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c index 78c9520017..9defc516bd 100644 --- a/src/udev/udevadm-info.c +++ b/src/udev/udevadm-info.c @@ -530,7 +530,7 @@ static int setup_matches(sd_device_enumerator *e) { STRV_FOREACH(a, arg_attr_match) { _cleanup_free_ char *k = NULL, *v = NULL; - r = parse_key_value_argument(*a, /* require_value= */ true, &k, &v); + r = parse_key_value_argument(*a, /* require_value= */ false, &k, &v); if (r < 0) return r; @@ -542,7 +542,7 @@ static int setup_matches(sd_device_enumerator *e) { STRV_FOREACH(a, arg_attr_nomatch) { _cleanup_free_ char *k = NULL, *v = NULL; - r = parse_key_value_argument(*a, /* require_value= */ true, &k, &v); + r = parse_key_value_argument(*a, /* require_value= */ false, &k, &v); if (r < 0) return r; @@ -1118,10 +1118,6 @@ static int parse_argv(int argc, char *argv[]) { OPTION_LONG("attr-match", "FILE[=VALUE]", "Query devices that match an attribute"): - if (!strchr(opts.arg, '=')) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Expected = instead of '%s'", opts.arg); - r = strv_extend(&arg_attr_match, opts.arg); if (r < 0) return log_oom(); @@ -1129,10 +1125,6 @@ static int parse_argv(int argc, char *argv[]) { OPTION_LONG("attr-nomatch", "FILE[=VALUE]", "Query devices that do not match an attribute"): - if (!strchr(opts.arg, '=')) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Expected = instead of '%s'", opts.arg); - r = strv_extend(&arg_attr_nomatch, opts.arg); if (r < 0) return log_oom(); diff --git a/test/units/TEST-17-UDEV.sanity-check.sh b/test/units/TEST-17-UDEV.sanity-check.sh index c0bd2b1565..5fefa56749 100755 --- a/test/units/TEST-17-UDEV.sanity-check.sh +++ b/test/units/TEST-17-UDEV.sanity-check.sh @@ -120,7 +120,9 @@ udevadm info -e --json=short | jq . >/dev/null udevadm info -e --subsystem-match acpi >/dev/null udevadm info -e --subsystem-nomatch acpi >/dev/null udevadm info -e --attr-match ifindex=2 >/dev/null +udevadm info -e --attr-match ifindex >/dev/null udevadm info -e --attr-nomatch ifindex=2 >/dev/null +udevadm info -e --attr-nomatch ifindex >/dev/null udevadm info -e --property-match SUBSYSTEM=acpi >/dev/null udevadm info -e --tag-match systemd >/dev/null udevadm info -e --sysname-match lo >/dev/null From 7bd6b8c5878cca9631b1ebbf93d9c79acaa5c586 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Mon, 13 Jul 2026 10:36:10 +0800 Subject: [PATCH 3/7] udevadm-wait: let --removed override initialization The documentation says --initialized= is ignored when --removed is specified. Track --removed separately so it wins regardless of option order. Reproducer: udevadm wait --timeout=0 --removed --initialized=no /dev/no-such-test-device Before: Timed out for waiting devices being added. Follow-up: aa2b0d8d291a1f1dc2b50016c076ff8196989f84 --- src/udev/udevadm-wait.c | 6 +++++- test/units/TEST-17-UDEV.sanity-check.sh | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/udev/udevadm-wait.c b/src/udev/udevadm-wait.c index fa12e6c98c..08c142d5eb 100644 --- a/src/udev/udevadm-wait.c +++ b/src/udev/udevadm-wait.c @@ -32,6 +32,7 @@ typedef enum WaitUntil { static WaitUntil arg_wait_until = WAIT_UNTIL_INITIALIZED; static usec_t arg_timeout_usec = USEC_INFINITY; +static bool arg_removed = false; static bool arg_settle = false; static char **arg_devices = NULL; @@ -350,7 +351,7 @@ static int parse_argv(int argc, char *argv[]) { break; OPTION_LONG("removed", NULL, "Wait for devices being removed"): - arg_wait_until = WAIT_UNTIL_REMOVED; + arg_removed = true; break; OPTION_LONG("settle", NULL, "Also wait for all queued events being processed"): @@ -358,6 +359,9 @@ static int parse_argv(int argc, char *argv[]) { break; } + if (arg_removed) + arg_wait_until = WAIT_UNTIL_REMOVED; + char **args = option_parser_get_args(&opts); if (strv_isempty(args)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), diff --git a/test/units/TEST-17-UDEV.sanity-check.sh b/test/units/TEST-17-UDEV.sanity-check.sh index 5fefa56749..976408fef2 100755 --- a/test/units/TEST-17-UDEV.sanity-check.sh +++ b/test/units/TEST-17-UDEV.sanity-check.sh @@ -303,6 +303,9 @@ udevadm wait -t 5 /sys/class/net/$netdev udevadm wait --initialized true /sys/class/net/$netdev udevadm wait --initialized false /sys/class/net/$netdev (! udevadm wait --initialized hello /sys/class/net/$netdev) +udevadm wait --timeout=0 --removed /dev/no-such-test-device +udevadm wait --timeout=0 --removed --initialized=no /dev/no-such-test-device +udevadm wait --timeout=0 --initialized=no --removed /dev/no-such-test-device assert_rc 124 timeout 5 udevadm wait --removed /sys/class/net/$netdev udevadm wait --settle /sys/class/net/$netdev udevadm wait -h From 778f93203200098d93879df2c4bede173f102d07 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Mon, 13 Jul 2026 10:38:46 +0800 Subject: [PATCH 4/7] udevadm-settle: reject positional arguments The settle command does not define positional arguments. Reject them during argument parsing instead of silently ignoring them. Reproducer: udevadm settle /no/such/argument Before: The command exits successfully and ignores the argument. Follow-up: c71509028fc2741b25dd537f9e1b7e8896df745e --- src/udev/udevadm-settle.c | 4 ++++ test/units/TEST-17-UDEV.sanity-check.sh | 1 + 2 files changed, 5 insertions(+) diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c index 77882cc074..c23e87c9d6 100644 --- a/src/udev/udevadm-settle.c +++ b/src/udev/udevadm-settle.c @@ -88,6 +88,10 @@ static int parse_argv(int argc, char *argv[]) { opts.opt->short_code); } + if (option_parser_get_n_args(&opts) > 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "This subprogram takes no positional arguments."); + return 1; } diff --git a/test/units/TEST-17-UDEV.sanity-check.sh b/test/units/TEST-17-UDEV.sanity-check.sh index 976408fef2..7de2a36722 100755 --- a/test/units/TEST-17-UDEV.sanity-check.sh +++ b/test/units/TEST-17-UDEV.sanity-check.sh @@ -152,6 +152,7 @@ udevadm monitor -h udevadm settle udevadm settle -t 5 udevadm settle -E /sys/class/net/$netdev +(! udevadm settle /no/such/argument) udevadm settle -h udevadm test /dev/null From e8dd0bfbbe77da2ae643b265749ce82cb23e5328 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Mon, 13 Jul 2026 10:41:03 +0800 Subject: [PATCH 5/7] man/udevadm: update device-id-of-file arguments udevadm info rejects positional devices together with --device-id-of-file=. Document that behavior instead of saying positional arguments are ignored. Reproducer: udevadm info --device-id-of-file=/etc/passwd /sys Before: Devices are not allowed with -d/--device-id-of-file and -c/--cleanup-db. Follow-up: 31767b92a0b3980e0ae8a0f44715f86e72c35f77 --- man/udevadm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/udevadm.xml b/man/udevadm.xml index c9a30901ed..2a9963cd14 100644 --- a/man/udevadm.xml +++ b/man/udevadm.xml @@ -240,7 +240,7 @@ Print major/minor numbers of the underlying device, where the file lives on. - If this is specified, all positional arguments are ignored. + If this is specified, no positional arguments are accepted. From 20e1e2de67720f2b6521054dbffa26e27a0d3f8d Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Mon, 13 Jul 2026 10:46:55 +0800 Subject: [PATCH 6/7] udev-config: merge configured children max Merge children_max from the per-source UdevConfig values before defaulting it from system resources. Reproducer: systemd-udevd --debug --children-max=1 Before: Set children_max to 32 Follow-up: 04fa5c1580ad388af3477ecfd7d4aa7d7f5cee30 --- src/udev/test-udev-manager.c | 13 +++++++++++++ src/udev/udev-config.c | 1 + 2 files changed, 14 insertions(+) diff --git a/src/udev/test-udev-manager.c b/src/udev/test-udev-manager.c index 7de5a37289..c2e70d3c8d 100644 --- a/src/udev/test-udev-manager.c +++ b/src/udev/test-udev-manager.c @@ -2,6 +2,7 @@ #include "alloc-util.h" #include "tests.h" +#include "udev-config.h" #include "udev-manager.h" TEST(devpath_conflict) { @@ -35,4 +36,16 @@ TEST(worker_free_detaches_event) { ASSERT_NULL(event->worker); } +TEST(manager_load_merges_children_max) { + _cleanup_(manager_freep) Manager *manager = ASSERT_PTR(manager_new()); + char *argv[] = { + (char*) "systemd-udevd", + (char*) "--children-max=1", + NULL, + }; + + ASSERT_OK_POSITIVE(manager_load(manager, ELEMENTSOF(argv) - 1, argv)); + ASSERT_EQ(manager->config.children_max, 1U); +} + DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/udev/udev-config.c b/src/udev/udev-config.c index 27a72f2ac8..c108985c4d 100644 --- a/src/udev/udev-config.c +++ b/src/udev/udev-config.c @@ -281,6 +281,7 @@ static void manager_merge_config(Manager *manager) { manager_merge_config_log_level(manager); MERGE_NON_NEGATIVE(resolve_name_timing, RESOLVE_NAME_EARLY); + MERGE_NON_ZERO(children_max, 0); MERGE_NON_ZERO(exec_delay_usec, 0); MERGE_NON_ZERO(timeout_usec, DEFAULT_WORKER_TIMEOUT_USEC); MERGE_NON_ZERO(timeout_signal, SIGKILL); From 4106cf743989e1460ef247c56e9341bb8f720da2 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Mon, 13 Jul 2026 10:53:48 +0800 Subject: [PATCH 7/7] udevadm-trigger: reject invalid wait-daemon timeout Return from argument parsing when --wait-daemon= cannot be parsed. This matches the other timeout options. Reproducer: udevadm trigger --dry-run --wait-daemon=bad Before: Failed to parse timeout value 'bad', ignoring: Invalid argument Follow-up: 2001622c58c1989f386086d11bd2a00d5fe00a30 --- src/udev/udevadm-trigger.c | 2 +- test/units/TEST-17-UDEV.sanity-check.sh | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c index e1fdf323cb..f1cdbfe105 100644 --- a/src/udev/udevadm-trigger.c +++ b/src/udev/udevadm-trigger.c @@ -469,7 +469,7 @@ static int parse_argv(int argc, char *argv[]) { if (opts.arg) { r = parse_sec(opts.arg, &arg_ping_timeout_usec); if (r < 0) - log_error_errno(r, "Failed to parse timeout value '%s', ignoring: %m", opts.arg); + return log_error_errno(r, "Failed to parse timeout value '%s': %m", opts.arg); } break; diff --git a/test/units/TEST-17-UDEV.sanity-check.sh b/test/units/TEST-17-UDEV.sanity-check.sh index 7de2a36722..d283aae318 100755 --- a/test/units/TEST-17-UDEV.sanity-check.sh +++ b/test/units/TEST-17-UDEV.sanity-check.sh @@ -282,6 +282,7 @@ udevadm trigger --parent-match /sys/class/net/$netdev --name-match /dev/null udevadm trigger --initialized-match udevadm trigger --initialized-nomatch udevadm trigger -w +(! udevadm trigger --dry-run --wait-daemon=bad) udevadm trigger --uuid /sys/class/net/$netdev udevadm settle -t 300 udevadm trigger --wait-daemon