From ab1333b2b7eefb68e3fe679c8cb60d17b81f556d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 1 Jul 2025 11:05:18 +0900 Subject: [PATCH] udev-rules: do not read udev rules files outside of specified root directory --- src/udev/fuzz-udev-rules.c | 9 ++++++- src/udev/udev-rules.c | 54 ++++++++++++++++++-------------------- src/udev/udev-rules.h | 2 +- src/udev/udevadm-verify.c | 4 +-- 4 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/udev/fuzz-udev-rules.c b/src/udev/fuzz-udev-rules.c index da94cdaf7e1..34567471e87 100644 --- a/src/udev/fuzz-udev-rules.c +++ b/src/udev/fuzz-udev-rules.c @@ -2,8 +2,11 @@ #include +#include "chase.h" +#include "conf-files.h" #include "fd-util.h" #include "fuzz.h" +#include "tests.h" #include "tmpfile-util.h" #include "udev-rules.h" @@ -24,7 +27,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { fflush(f); assert_se(rules = udev_rules_new(RESOLVE_NAME_EARLY)); - r = udev_rules_parse_file(rules, filename, /* extra_checks = */ false, NULL); + + _cleanup_(conf_file_freep) ConfFile *c = NULL; + ASSERT_OK(conf_file_new(filename, /* root = */ NULL, CHASE_MUST_BE_REGULAR, &c)); + + r = udev_rules_parse_file(rules, c, /* extra_checks = */ false, /* ret = */ NULL); log_info_errno(r, "Parsing %s: %m", filename); assert_se(r >= 0 || /* OK */ r == -ENOBUFS); /* line length exceeded */ diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index a6da5074ac6..b7fc0a675c5 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -1645,46 +1645,34 @@ static void udev_check_rule_line(UdevRuleLine *line) { udev_check_conflicts_duplicates(line); } -int udev_rules_parse_file(UdevRules *rules, const char *filename, bool extra_checks, UdevRuleFile **ret) { +int udev_rules_parse_file(UdevRules *rules, const ConfFile *c, bool extra_checks, UdevRuleFile **ret) { _cleanup_(udev_rule_file_freep) UdevRuleFile *rule_file = NULL; _cleanup_free_ char *name = NULL; _cleanup_fclose_ FILE *f = NULL; - struct stat st; int r; assert(rules); - assert(filename); + assert(c); + assert(c->fd >= 0); + assert(c->original_path); - f = fopen(filename, "re"); + f = fopen(FORMAT_PROC_FD_PATH(c->fd), "re"); if (!f) { if (extra_checks) return -errno; - if (errno == ENOENT) - return 0; - - return log_warning_errno(errno, "Failed to open %s, ignoring: %m", filename); - } - - if (fstat(fileno(f), &st) < 0) - return log_warning_errno(errno, "Failed to stat %s, ignoring: %m", filename); - - if (null_or_empty(&st)) { - log_debug("Skipping empty file: %s", filename); - if (ret) - *ret = NULL; - return 0; + return log_warning_errno(errno, "Failed to open %s, ignoring: %m", c->original_path); } - r = hashmap_put_stats_by_path(&rules->stats_by_path, filename, &st); + r = hashmap_put_stats_by_path(&rules->stats_by_path, c->original_path, &c->st); if (r < 0) - return log_warning_errno(r, "Failed to save stat for %s, ignoring: %m", filename); + return log_warning_errno(r, "Failed to save stat for %s, ignoring: %m", c->original_path); - (void) fd_warn_permissions(filename, fileno(f)); + (void) stat_warn_permissions(c->original_path, &c->st); - log_debug("Reading rules file: %s", filename); + log_debug("Reading rules file: %s", c->original_path); - name = strdup(filename); + name = strdup(c->original_path); if (!name) return log_oom(); @@ -1775,7 +1763,7 @@ int udev_rules_parse_file(UdevRules *rules, const char *filename, bool extra_che *ret = rule_file; TAKE_PTR(rule_file); - return 1; + return 0; } unsigned udev_rule_file_get_issues(UdevRuleFile *rule_file) { @@ -1800,7 +1788,7 @@ UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing) { int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing, char * const *extra) { _cleanup_(udev_rules_freep) UdevRules *rules = NULL; - _cleanup_strv_free_ char **files = NULL, **directories = NULL; + _cleanup_strv_free_ char **directories = NULL; int r; rules = udev_rules_new(resolve_name_timing); @@ -1817,14 +1805,22 @@ int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing if (r < 0) return r; - r = conf_files_list_strv(&files, ".rules", NULL, 0, (const char* const*) directories); + ConfFile **files = NULL; + size_t n_files = 0; + + CLEANUP_ARRAY(files, n_files, conf_file_free_many); + + r = conf_files_list_strv_full(".rules", /* root = */ NULL, CONF_FILES_REGULAR | CONF_FILES_FILTER_MASKED, + (const char* const*) directories, &files, &n_files); if (r < 0) return log_debug_errno(r, "Failed to enumerate rules files: %m"); - STRV_FOREACH(f, files) { - r = udev_rules_parse_file(rules, *f, /* extra_checks = */ false, NULL); + FOREACH_ARRAY(i, files, n_files) { + ConfFile *c = *i; + + r = udev_rules_parse_file(rules, c, /* extra_checks = */ false, /* ret = */ NULL); if (r < 0) - log_debug_errno(r, "Failed to read rules file %s, ignoring: %m", *f); + log_debug_errno(r, "Failed to read rules file '%s', ignoring: %m", c->original_path); } *ret_rules = TAKE_PTR(rules); diff --git a/src/udev/udev-rules.h b/src/udev/udev-rules.h index d0ed2adf00e..19d6e7e0e15 100644 --- a/src/udev/udev-rules.h +++ b/src/udev/udev-rules.h @@ -5,7 +5,7 @@ #include "udev-forward.h" int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos, bool *ret_is_case_insensitive); -int udev_rules_parse_file(UdevRules *rules, const char *filename, bool extra_checks, UdevRuleFile **ret); +int udev_rules_parse_file(UdevRules *rules, const ConfFile *c, bool extra_checks, UdevRuleFile **ret); unsigned udev_rule_file_get_issues(UdevRuleFile *rule_file); UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing); int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing, char * const *extra); diff --git a/src/udev/udevadm-verify.c b/src/udev/udevadm-verify.c index 86bb6847ce6..15406357b5e 100644 --- a/src/udev/udevadm-verify.c +++ b/src/udev/udevadm-verify.c @@ -108,11 +108,9 @@ static int verify_rules_file(UdevRules *rules, const ConfFile *c) { assert(rules); assert(c); - r = udev_rules_parse_file(rules, c->resolved_path, /* extra_checks = */ true, &file); + r = udev_rules_parse_file(rules, c, /* extra_checks = */ true, &file); if (r < 0) return log_error_errno(r, "Failed to parse rules file %s: %m", c->original_path); - if (r == 0) /* empty file. */ - return 0; unsigned issues = udev_rule_file_get_issues(file); unsigned mask = (1U << LOG_ERR) | (1U << LOG_WARNING); -- 2.47.3