]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev-rules: do not read udev rules files outside of specified root directory
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 1 Jul 2025 02:05:18 +0000 (11:05 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 11 Jul 2025 01:42:08 +0000 (10:42 +0900)
src/udev/fuzz-udev-rules.c
src/udev/udev-rules.c
src/udev/udev-rules.h
src/udev/udevadm-verify.c

index da94cdaf7e133afe27dbdd3702c2d3542f3fe4cb..34567471e8740922c0a7cf5569daba9d76f968d3 100644 (file)
@@ -2,8 +2,11 @@
 
 #include <stdio.h>
 
+#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 */
index a6da5074ac647f11360d9442fabb28952fc9610f..b7fc0a675c57404a98c9fd635e86c7cb4c689810 100644 (file)
@@ -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);
index d0ed2adf00e1ec4cfbb465beaa94d8ba81c0ef5d..19d6e7e0e1598b70906ae08f49672ec233cfe7ec 100644 (file)
@@ -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);
index 86bb6847ce6927c90f4868af498f32250509e4bc..15406357b5e5b86c6f3794d87e320584f204c3d2 100644 (file)
@@ -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);