]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: warn when result of string substitution is truncated
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 13 Dec 2021 23:05:41 +0000 (08:05 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 25 Dec 2021 06:13:17 +0000 (15:13 +0900)
src/udev/udev-event.c
src/udev/udev-event.h
src/udev/udev-rules.c
src/udev/udevadm-test.c

index a60e4f294c8ef6973e859ad293ff77f7a83b696d..3cb830232554a6c5342f7a63f03b48d28f375994 100644 (file)
@@ -237,9 +237,12 @@ static ssize_t udev_event_subst_format(
                 FormatSubstitutionType type,
                 const char *attr,
                 char *dest,
-                size_t l) {
+                size_t l,
+                bool *ret_truncated) {
+
         sd_device *parent, *dev = event->dev;
         const char *val = NULL;
+        bool truncated = false;
         char *s = dest;
         int r;
 
@@ -248,13 +251,13 @@ static ssize_t udev_event_subst_format(
                 r = sd_device_get_devpath(dev, &val);
                 if (r < 0)
                         return r;
-                strpcpy(&s, l, val);
+                strpcpy_full(&s, l, val, &truncated);
                 break;
         case FORMAT_SUBST_KERNEL:
                 r = sd_device_get_sysname(dev, &val);
                 if (r < 0)
                         return r;
-                strpcpy(&s, l, val);
+                strpcpy_full(&s, l, val, &truncated);
                 break;
         case FORMAT_SUBST_KERNEL_NUMBER:
                 r = sd_device_get_sysnum(dev, &val);
@@ -262,7 +265,7 @@ static ssize_t udev_event_subst_format(
                         goto null_terminate;
                 if (r < 0)
                         return r;
-                strpcpy(&s, l, val);
+                strpcpy_full(&s, l, val, &truncated);
                 break;
         case FORMAT_SUBST_ID:
                 if (!event->dev_parent)
@@ -270,7 +273,7 @@ static ssize_t udev_event_subst_format(
                 r = sd_device_get_sysname(event->dev_parent, &val);
                 if (r < 0)
                         return r;
-                strpcpy(&s, l, val);
+                strpcpy_full(&s, l, val, &truncated);
                 break;
         case FORMAT_SUBST_DRIVER:
                 if (!event->dev_parent)
@@ -280,7 +283,7 @@ static ssize_t udev_event_subst_format(
                         goto null_terminate;
                 if (r < 0)
                         return r;
-                strpcpy(&s, l, val);
+                strpcpy_full(&s, l, val, &truncated);
                 break;
         case FORMAT_SUBST_MAJOR:
         case FORMAT_SUBST_MINOR: {
@@ -289,7 +292,7 @@ static ssize_t udev_event_subst_format(
                 r = sd_device_get_devnum(dev, &devnum);
                 if (r < 0 && r != -ENOENT)
                         return r;
-                strpcpyf(&s, l, "%u", r < 0 ? 0 : type == FORMAT_SUBST_MAJOR ? major(devnum) : minor(devnum));
+                strpcpyf_full(&s, l, &truncated, "%u", r < 0 ? 0 : type == FORMAT_SUBST_MAJOR ? major(devnum) : minor(devnum));
                 break;
         }
         case FORMAT_SUBST_RESULT: {
@@ -308,7 +311,7 @@ static ssize_t udev_event_subst_format(
                 }
 
                 if (index == 0)
-                        strpcpy(&s, l, event->program_result);
+                        strpcpy_full(&s, l, event->program_result, &truncated);
                 else {
                         const char *start, *p;
                         unsigned i;
@@ -330,11 +333,11 @@ static ssize_t udev_event_subst_format(
                         start = p;
                         /* %c{2+} copies the whole string from the second part on */
                         if (has_plus)
-                                strpcpy(&s, l, start);
+                                strpcpy_full(&s, l, start, &truncated);
                         else {
                                 while (*p && !strchr(WHITESPACE, *p))
                                         p++;
-                                strnpcpy(&s, l, start, p - start);
+                                strnpcpy_full(&s, l, start, p - start, &truncated);
                         }
                 }
                 break;
@@ -342,6 +345,7 @@ static ssize_t udev_event_subst_format(
         case FORMAT_SUBST_ATTR: {
                 char vbuf[UDEV_NAME_SIZE];
                 int count;
+                bool t;
 
                 if (isempty(attr))
                         return -EINVAL;
@@ -363,12 +367,13 @@ static ssize_t udev_event_subst_format(
 
                 /* strip trailing whitespace, and replace unwanted characters */
                 if (val != vbuf)
-                        strscpy(vbuf, sizeof(vbuf), val);
+                        strscpy_full(vbuf, sizeof(vbuf), val, &truncated);
                 delete_trailing_chars(vbuf, NULL);
                 count = udev_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT);
                 if (count > 0)
                         log_device_debug(dev, "%i character(s) replaced", count);
-                strpcpy(&s, l, vbuf);
+                strpcpy_full(&s, l, vbuf, &t);
+                truncated = truncated || t;
                 break;
         }
         case FORMAT_SUBST_PARENT:
@@ -382,7 +387,7 @@ static ssize_t udev_event_subst_format(
                         goto null_terminate;
                 if (r < 0)
                         return r;
-                strpcpy(&s, l, val + STRLEN("/dev/"));
+                strpcpy_full(&s, l, val + STRLEN("/dev/"), &truncated);
                 break;
         case FORMAT_SUBST_DEVNODE:
                 r = sd_device_get_devname(dev, &val);
@@ -390,34 +395,37 @@ static ssize_t udev_event_subst_format(
                         goto null_terminate;
                 if (r < 0)
                         return r;
-                strpcpy(&s, l, val);
+                strpcpy_full(&s, l, val, &truncated);
                 break;
         case FORMAT_SUBST_NAME:
                 if (event->name)
-                        strpcpy(&s, l, event->name);
+                        strpcpy_full(&s, l, event->name, &truncated);
                 else if (sd_device_get_devname(dev, &val) >= 0)
-                        strpcpy(&s, l, val + STRLEN("/dev/"));
+                        strpcpy_full(&s, l, val + STRLEN("/dev/"), &truncated);
                 else {
                         r = sd_device_get_sysname(dev, &val);
                         if (r < 0)
                                 return r;
-                        strpcpy(&s, l, val);
+                        strpcpy_full(&s, l, val, &truncated);
                 }
                 break;
         case FORMAT_SUBST_LINKS:
-                FOREACH_DEVICE_DEVLINK(dev, val)
+                FOREACH_DEVICE_DEVLINK(dev, val) {
                         if (s == dest)
-                                strpcpy(&s, l, val + STRLEN("/dev/"));
+                                strpcpy_full(&s, l, val + STRLEN("/dev/"), &truncated);
                         else
-                                strpcpyl(&s, l, " ", val + STRLEN("/dev/"), NULL);
+                                strpcpyl_full(&s, l, &truncated, " ", val + STRLEN("/dev/"), NULL);
+                        if (truncated)
+                                break;
+                }
                 if (s == dest)
                         goto null_terminate;
                 break;
         case FORMAT_SUBST_ROOT:
-                strpcpy(&s, l, "/dev");
+                strpcpy_full(&s, l, "/dev", &truncated);
                 break;
         case FORMAT_SUBST_SYS:
-                strpcpy(&s, l, "/sys");
+                strpcpy_full(&s, l, "/sys", &truncated);
                 break;
         case FORMAT_SUBST_ENV:
                 if (isempty(attr))
@@ -427,22 +435,34 @@ static ssize_t udev_event_subst_format(
                         goto null_terminate;
                 if (r < 0)
                         return r;
-                strpcpy(&s, l, val);
+                strpcpy_full(&s, l, val, &truncated);
                 break;
         default:
                 assert_not_reached();
         }
 
+        if (ret_truncated)
+                *ret_truncated = truncated;
+
         return s - dest;
 
 null_terminate:
+        if (ret_truncated)
+                *ret_truncated = truncated;
+
         *s = '\0';
         return 0;
 }
 
-size_t udev_event_apply_format(UdevEvent *event,
-                               const char *src, char *dest, size_t size,
-                               bool replace_whitespace) {
+size_t udev_event_apply_format(
+                UdevEvent *event,
+                const char *src,
+                char *dest,
+                size_t size,
+                bool replace_whitespace,
+                bool *ret_truncated) {
+
+        bool truncated = false;
         const char *s = src;
         int r;
 
@@ -456,20 +476,24 @@ size_t udev_event_apply_format(UdevEvent *event,
                 FormatSubstitutionType type;
                 char attr[UDEV_PATH_SIZE];
                 ssize_t subst_len;
+                bool t;
 
                 r = get_subst_type(&s, false, &type, attr);
                 if (r < 0) {
                         log_device_warning_errno(event->dev, r, "Invalid format string, ignoring: %s", src);
                         break;
                 } else if (r == 0) {
-                        if (size < 2) /* need space for this char and the terminating NUL */
+                        if (size < 2) {
+                                /* need space for this char and the terminating NUL */
+                                truncated = true;
                                 break;
+                        }
                         *dest++ = *s++;
                         size--;
                         continue;
                 }
 
-                subst_len = udev_event_subst_format(event, type, attr, dest, size);
+                subst_len = udev_event_subst_format(event, type, attr, dest, size, &t);
                 if (subst_len < 0) {
                         log_device_warning_errno(event->dev, subst_len,
                                                  "Failed to substitute variable '$%s' or apply format '%%%c', ignoring: %m",
@@ -477,6 +501,8 @@ size_t udev_event_apply_format(UdevEvent *event,
                         break;
                 }
 
+                truncated = truncated || t;
+
                 /* FORMAT_SUBST_RESULT handles spaces itself */
                 if (replace_whitespace && type != FORMAT_SUBST_RESULT)
                         /* udev_replace_whitespace can replace in-place,
@@ -488,6 +514,10 @@ size_t udev_event_apply_format(UdevEvent *event,
         }
 
         assert(size >= 1);
+
+        if (ret_truncated)
+                *ret_truncated = truncated;
+
         *dest = '\0';
         return size;
 }
index 2067909fde4c61391620b0f54462b7df216d1d4d..823055ddb237512b776dfa837cd59cb741738c6c 100644 (file)
@@ -56,7 +56,8 @@ size_t udev_event_apply_format(
                 const char *src,
                 char *dest,
                 size_t size,
-                bool replace_whitespace);
+                bool replace_whitespace,
+                bool *ret_truncated);
 int udev_check_format(const char *value, size_t *offset, const char **hint);
 int udev_event_spawn(
                 UdevEvent *event,
index 1a384d6b38451787d5c1f6b24721d00460984ac9..b353b5b8106c4181c0fd96f5085fb8d0e4fcb099 100644 (file)
@@ -1381,11 +1381,14 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
         return token->op == (match ? OP_MATCH : OP_NOMATCH);
 }
 
-static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *event) {
+static bool token_match_attr(UdevRules *rules, UdevRuleToken *token, sd_device *dev, UdevEvent *event) {
         char nbuf[UDEV_NAME_SIZE], vbuf[UDEV_NAME_SIZE];
         const char *name, *value;
+        bool truncated;
 
+        assert(rules);
         assert(token);
+        assert(IN_SET(token->type, TK_M_ATTR, TK_M_PARENTS_ATTR));
         assert(dev);
         assert(event);
 
@@ -1393,7 +1396,15 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
 
         switch (token->attr_subst_type) {
         case SUBST_TYPE_FORMAT:
-                (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false);
+                (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false, &truncated);
+                if (truncated) {
+                        log_rule_debug(dev, rules,
+                                       "The sysfs attribute name '%s' is truncated while substituting into '%s', "
+                                       "assuming the %s key does not match.", nbuf, name,
+                                       token->type == TK_M_ATTR ? "ATTR" : "ATTRS");
+                        return false;
+                }
+
                 name = nbuf;
                 _fallthrough_;
         case SUBST_TYPE_PLAIN:
@@ -1497,19 +1508,22 @@ static int attr_subst_subdir(char attr[static UDEV_PATH_SIZE]) {
         char buf[UDEV_PATH_SIZE], *p;
         const char *tail;
         size_t len, size;
+        bool truncated;
 
         assert(attr);
 
         tail = strstr(attr, "/*/");
         if (!tail)
-            return 0;
+                return 0;
 
         len = tail - attr + 1; /* include slash at the end */
         tail += 2; /* include slash at the beginning */
 
         p = buf;
         size = sizeof(buf);
-        size -= strnpcpy(&p, size, attr, len);
+        size -= strnpcpy_full(&p, size, attr, len, &truncated);
+        if (truncated)
+                return -ENOENT;
 
         dir = opendir(buf);
         if (!dir)
@@ -1519,7 +1533,10 @@ static int attr_subst_subdir(char attr[static UDEV_PATH_SIZE]) {
                 if (de->d_name[0] == '.')
                         continue;
 
-                strscpyl(p, size, de->d_name, tail, NULL);
+                strscpyl_full(p, size, &truncated, de->d_name, tail, NULL);
+                if (truncated)
+                        continue;
+
                 if (faccessat(dirfd(dir), p, F_OK, 0) < 0)
                         continue;
 
@@ -1645,12 +1662,19 @@ static int udev_rule_apply_token_to_event(
         }
         case TK_M_ATTR:
         case TK_M_PARENTS_ATTR:
-                return token_match_attr(token, dev, event);
+                return token_match_attr(rules, token, dev, event);
         case TK_M_SYSCTL: {
                 _cleanup_free_ char *value = NULL;
                 char buf[UDEV_PATH_SIZE];
+                bool truncated;
+
+                (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated);
+                if (truncated) {
+                        log_rule_debug(dev, rules, "The sysctl entry name '%s' is truncated while substituting into '%s', "
+                                       "assuming the SYSCTL key does not match.", buf, (const char*) token->data);
+                        return false;
+                }
 
-                (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false);
                 r = sysctl_read(sysctl_normalize(buf), &value);
                 if (r < 0 && r != -ENOENT)
                         return log_rule_error_errno(dev, rules, r, "Failed to read sysctl '%s': %m", buf);
@@ -1661,9 +1685,15 @@ static int udev_rule_apply_token_to_event(
                 mode_t mode = PTR_TO_MODE(token->data);
                 char buf[UDEV_PATH_SIZE];
                 struct stat statbuf;
-                bool match;
+                bool match, truncated;
+
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                if (truncated) {
+                        log_rule_debug(dev, rules, "The file name '%s' is truncated while substituting into '%s', "
+                                       "assuming the TEST key does not match", buf, token->value);
+                        return false;
+                }
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
                 if (!path_is_absolute(buf) &&
                     udev_resolve_subsys_kernel(buf, buf, sizeof(buf), false) < 0) {
                         char tmp[UDEV_PATH_SIZE];
@@ -1673,8 +1703,11 @@ static int udev_rule_apply_token_to_event(
                         if (r < 0)
                                 return log_rule_error_errno(dev, rules, r, "Failed to get syspath: %m");
 
-                        strscpy(tmp, sizeof(tmp), buf);
-                        strscpyl(buf, sizeof(buf), val, "/", tmp, NULL);
+                        strscpy_full(tmp, sizeof(tmp), buf, &truncated);
+                        assert(!truncated);
+                        strscpyl_full(buf, sizeof(buf), &truncated, val, "/", tmp, NULL);
+                        if (truncated)
+                                return false;
                 }
 
                 r = attr_subst_subdir(buf);
@@ -1694,10 +1727,17 @@ static int udev_rule_apply_token_to_event(
         }
         case TK_M_PROGRAM: {
                 char buf[UDEV_PATH_SIZE], result[UDEV_LINE_SIZE];
+                bool truncated;
                 size_t count;
 
                 event->program_result = mfree(event->program_result);
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                if (truncated) {
+                        log_rule_debug(dev, rules, "The command '%s' is trucated while substituting into '%s', "
+                                       "assuming the PROGRAM key does not match.", buf, token->value);
+                        return false;
+                }
+
                 log_rule_debug(dev, rules, "Running PROGRAM '%s'", buf);
 
                 r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof(result));
@@ -1721,8 +1761,15 @@ static int udev_rule_apply_token_to_event(
         case TK_M_IMPORT_FILE: {
                 _cleanup_fclose_ FILE *f = NULL;
                 char buf[UDEV_PATH_SIZE];
+                bool truncated;
+
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                if (truncated) {
+                        log_rule_debug(dev, rules, "The file name '%s' to be imported is truncated while substituting into '%s', "
+                                       "assuming the IMPORT key does not match.", buf, token->value);
+                        return false;
+                }
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
                 log_rule_debug(dev, rules, "Importing properties from '%s'", buf);
 
                 f = fopen(buf, "re");
@@ -1768,8 +1815,15 @@ static int udev_rule_apply_token_to_event(
         case TK_M_IMPORT_PROGRAM: {
                 _cleanup_strv_free_ char **lines = NULL;
                 char buf[UDEV_PATH_SIZE], result[UDEV_LINE_SIZE], **line;
+                bool truncated;
+
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                if (truncated) {
+                        log_rule_debug(dev, rules, "The command '%s' is truncated while substituting into '%s', "
+                                       "assuming the IMPORT key does not match.", buf, token->value);
+                        return false;
+                }
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
                 log_rule_debug(dev, rules, "Importing properties from results of '%s'", buf);
 
                 r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof result);
@@ -1813,6 +1867,7 @@ static int udev_rule_apply_token_to_event(
                 assert(cmd >= 0 && cmd < _UDEV_BUILTIN_MAX);
                 unsigned mask = 1U << (int) cmd;
                 char buf[UDEV_PATH_SIZE];
+                bool truncated;
 
                 if (udev_builtin_run_once(cmd)) {
                         /* check if we ran already */
@@ -1826,7 +1881,13 @@ static int udev_rule_apply_token_to_event(
                         event->builtin_run |= mask;
                 }
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                if (truncated) {
+                        log_rule_debug(dev, rules, "The builtin command '%s' is truncated while substituting into '%s', "
+                                       "assuming the IMPORT key does not match", buf, token->value);
+                        return false;
+                }
+
                 log_rule_debug(dev, rules, "Importing properties from results of builtin command '%s'", buf);
 
                 r = udev_builtin_run(dev, &event->rtnl, cmd, buf, false);
@@ -1875,8 +1936,15 @@ static int udev_rule_apply_token_to_event(
         }
         case TK_M_IMPORT_PARENT: {
                 char buf[UDEV_PATH_SIZE];
+                bool truncated;
+
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                if (truncated) {
+                        log_rule_debug(dev, rules, "The property name '%s' is truncated while substituting into '%s', "
+                                       "assuming the IMPORT key does not match.", buf, token->value);
+                        return false;
+                }
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
                 r = import_parent_into_properties(dev, buf);
                 if (r < 0)
                         return log_rule_error_errno(dev, rules, r,
@@ -1925,13 +1993,20 @@ static int udev_rule_apply_token_to_event(
         case TK_A_OWNER: {
                 char owner[UDEV_NAME_SIZE];
                 const char *ow = owner;
+                bool truncated;
 
                 if (event->owner_final)
                         break;
                 if (token->op == OP_ASSIGN_FINAL)
                         event->owner_final = true;
 
-                (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false);
+                (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false, &truncated);
+                if (truncated) {
+                        log_rule_warning(dev, rules, "The user name '%s' is truncated while substituting into '%s', "
+                                         "refusing to apply the OWNER key.", owner, token->value);
+                        break;
+                }
+
                 r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
                 if (r < 0)
                         log_unknown_owner(dev, rules, r, "user", owner);
@@ -1942,13 +2017,20 @@ static int udev_rule_apply_token_to_event(
         case TK_A_GROUP: {
                 char group[UDEV_NAME_SIZE];
                 const char *gr = group;
+                bool truncated;
 
                 if (event->group_final)
                         break;
                 if (token->op == OP_ASSIGN_FINAL)
                         event->group_final = true;
 
-                (void) udev_event_apply_format(event, token->value, group, sizeof(group), false);
+                (void) udev_event_apply_format(event, token->value, group, sizeof(group), false, &truncated);
+                if (truncated) {
+                        log_rule_warning(dev, rules, "The group name '%s' is truncated while substituting into '%s', "
+                                         "refusing to apply the GROUP key.", group, token->value);
+                        break;
+                }
+
                 r = get_group_creds(&gr, &event->gid, USER_CREDS_ALLOW_MISSING);
                 if (r < 0)
                         log_unknown_owner(dev, rules, r, "group", group);
@@ -1958,13 +2040,20 @@ static int udev_rule_apply_token_to_event(
         }
         case TK_A_MODE: {
                 char mode_str[UDEV_NAME_SIZE];
+                bool truncated;
 
                 if (event->mode_final)
                         break;
                 if (token->op == OP_ASSIGN_FINAL)
                         event->mode_final = true;
 
-                (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false);
+                (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false, &truncated);
+                if (truncated) {
+                        log_rule_warning(dev, rules, "The mode '%s' is truncated while substituting into %s, "
+                                         "refusing to apply the MODE key.", mode_str, token->value);
+                        break;
+                }
+
                 r = parse_mode(mode_str, &event->mode);
                 if (r < 0)
                         log_rule_error_errno(dev, rules, r, "Failed to parse mode '%s', ignoring: %m", mode_str);
@@ -2005,12 +2094,19 @@ static int udev_rule_apply_token_to_event(
         case TK_A_SECLABEL: {
                 _cleanup_free_ char *name = NULL, *label = NULL;
                 char label_str[UDEV_LINE_SIZE] = {};
+                bool truncated;
 
                 name = strdup(token->data);
                 if (!name)
                         return log_oom();
 
-                (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false);
+                (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false, &truncated);
+                if (truncated) {
+                        log_rule_warning(dev, rules, "The security label '%s' is truncated while substituting into '%s', "
+                                         "refusing to apply the SECLABEL key.", label_str, token->value);
+                        break;
+                }
+
                 if (!isempty(label_str))
                         label = strdup(label_str);
                 else
@@ -2037,6 +2133,7 @@ static int udev_rule_apply_token_to_event(
                 const char *val, *name = token->data;
                 char value_new[UDEV_NAME_SIZE], *p = value_new;
                 size_t count, l = sizeof(value_new);
+                bool truncated;
 
                 if (isempty(token->value)) {
                         if (token->op == OP_ADD)
@@ -2048,10 +2145,22 @@ static int udev_rule_apply_token_to_event(
                 }
 
                 if (token->op == OP_ADD &&
-                    sd_device_get_property_value(dev, name, &val) >= 0)
-                        l = strpcpyl(&p, l, val, " ", NULL);
+                    sd_device_get_property_value(dev, name, &val) >= 0) {
+                        l = strpcpyl_full(&p, l, &truncated, val, " ", NULL);
+                        if (truncated) {
+                                log_rule_warning(dev, rules, "The buffer for the property '%s' is full, "
+                                                 "refusing to append the new value '%s'.", name, token->value);
+                                break;
+                        }
+                }
+
+                (void) udev_event_apply_format(event, token->value, p, l, false, &truncated);
+                if (truncated) {
+                        log_rule_warning(dev, rules, "The property value '%s' is truncated while substituting into '%s', "
+                                         "refusing to add property '%s'.", p, token->value, name);
+                        break;
+                }
 
-                (void) udev_event_apply_format(event, token->value, p, l, false);
                 if (event->esc == ESCAPE_REPLACE) {
                         count = udev_replace_chars(p, NULL);
                         if (count > 0)
@@ -2066,8 +2175,16 @@ static int udev_rule_apply_token_to_event(
         }
         case TK_A_TAG: {
                 char buf[UDEV_PATH_SIZE];
+                bool truncated;
+
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                if (truncated) {
+                        log_rule_warning(dev, rules, "The tag name '%s' is truncated while substituting into '%s',"
+                                         "refusing to %s the tag.", buf, token->value,
+                                         token->op == OP_REMOVE ? "remove" : "add");
+                        break;
+                }
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
                 if (token->op == OP_ASSIGN)
                         device_cleanup_tags(dev);
 
@@ -2086,6 +2203,7 @@ static int udev_rule_apply_token_to_event(
         }
         case TK_A_NAME: {
                 char buf[UDEV_PATH_SIZE];
+                bool truncated;
                 size_t count;
 
                 if (event->name_final)
@@ -2100,7 +2218,13 @@ static int udev_rule_apply_token_to_event(
                         break;
                 }
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                if (truncated) {
+                        log_rule_warning(dev, rules, "The network interface name '%s' is truncated while substituting into '%s', "
+                                         "refusing to set the name.", buf, token->value);
+                        break;
+                }
+
                 if (IN_SET(event->esc, ESCAPE_UNSET, ESCAPE_REPLACE)) {
                         if (naming_scheme_has(NAMING_REPLACE_STRICTLY))
                                 count = udev_replace_ifname(buf);
@@ -2119,6 +2243,7 @@ static int udev_rule_apply_token_to_event(
         }
         case TK_A_DEVLINK: {
                 char buf[UDEV_PATH_SIZE], *p;
+                bool truncated;
                 size_t count;
 
                 if (event->devlink_final)
@@ -2131,7 +2256,13 @@ static int udev_rule_apply_token_to_event(
                         device_cleanup_devlinks(dev);
 
                 /* allow multiple symlinks separated by spaces */
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), event->esc != ESCAPE_NONE);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), event->esc != ESCAPE_NONE, &truncated);
+                if (truncated) {
+                        log_rule_warning(dev, rules, "The symbolic link path '%s' is truncated while substituting into '%s', "
+                                         "refusing to add the device symbolic link.", buf, token->value);
+                        break;
+                }
+
                 if (event->esc == ESCAPE_UNSET)
                         count = udev_replace_chars(buf, "/ ");
                 else if (event->esc == ESCAPE_REPLACE)
@@ -2152,7 +2283,10 @@ static int udev_rule_apply_token_to_event(
                                 next = skip_leading_chars(next, NULL);
                         }
 
-                        strscpyl(filename, sizeof(filename), "/dev/", p, NULL);
+                        strscpyl_full(filename, sizeof(filename), &truncated, "/dev/", p, NULL);
+                        if (truncated)
+                                continue;
+
                         r = device_add_devlink(dev, filename);
                         if (r < 0)
                                 return log_rule_error_errno(dev, rules, r, "Failed to add devlink '%s': %m", filename);
@@ -2165,17 +2299,30 @@ static int udev_rule_apply_token_to_event(
         case TK_A_ATTR: {
                 char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE];
                 const char *val, *key_name = token->data;
+                bool truncated;
 
                 if (udev_resolve_subsys_kernel(key_name, buf, sizeof(buf), false) < 0 &&
-                    sd_device_get_syspath(dev, &val) >= 0)
-                        strscpyl(buf, sizeof(buf), val, "/", key_name, NULL);
+                    sd_device_get_syspath(dev, &val) >= 0) {
+                        strscpyl_full(buf, sizeof(buf), &truncated, val, "/", key_name, NULL);
+                        if (truncated) {
+                                log_rule_warning(dev, rules,
+                                                 "The path to the attribute '%s/%s' is too long, refusing to set the attribute.",
+                                                 val, key_name);
+                                break;
+                        }
+                }
 
                 r = attr_subst_subdir(buf);
                 if (r < 0) {
                         log_rule_error_errno(dev, rules, r, "Could not find file matches '%s', ignoring: %m", buf);
                         break;
                 }
-                (void) udev_event_apply_format(event, token->value, value, sizeof(value), false);
+                (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated);
+                if (truncated) {
+                        log_rule_warning(dev, rules, "The attribute value '%s' is truncated while substituting into '%s', "
+                                         "refusing to set the attribute '%s'", value, token->value, buf);
+                        break;
+                }
 
                 log_rule_debug(dev, rules, "ATTR '%s' writing '%s'", buf, value);
                 r = write_string_file(buf, value,
@@ -2189,9 +2336,22 @@ static int udev_rule_apply_token_to_event(
         }
         case TK_A_SYSCTL: {
                 char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE];
+                bool truncated;
+
+                (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated);
+                if (truncated) {
+                        log_rule_warning(dev, rules, "The sysctl entry name '%s' is truncated while substituting into '%s', "
+                                         "refusing to set the sysctl entry.", buf, (const char*) token->data);
+                        break;
+                }
+
+                (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated);
+                if (truncated) {
+                        log_rule_warning(dev, rules, "The sysctl value '%s' is truncated while substituting into '%s', "
+                                         "refusing to set the sysctl entry '%s'", value, token->value, buf);
+                        break;
+                }
 
-                (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false);
-                (void) udev_event_apply_format(event, token->value, value, sizeof(value), false);
                 sysctl_normalize(buf);
                 log_rule_debug(dev, rules, "SYSCTL '%s' writing '%s'", buf, value);
                 r = sysctl_write(buf, value);
@@ -2203,6 +2363,7 @@ static int udev_rule_apply_token_to_event(
         case TK_A_RUN_PROGRAM: {
                 _cleanup_free_ char *cmd = NULL;
                 char buf[UDEV_PATH_SIZE];
+                bool truncated;
 
                 if (event->run_final)
                         break;
@@ -2212,7 +2373,12 @@ static int udev_rule_apply_token_to_event(
                 if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL))
                         ordered_hashmap_clear_free_key(event->run_list);
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                if (truncated) {
+                        log_rule_warning(dev, rules, "The command '%s' is truncated while substituting into '%s', "
+                                         "refusing to invoke the command.", buf, token->value);
+                        break;
+                }
 
                 cmd = strdup(buf);
                 if (!cmd)
index 01057e12563b1d11b6d991bfbb454b2b5152d1ec..8adebbc83e2375acfe85ecce01e858f70abf7c56 100644 (file)
@@ -135,8 +135,11 @@ int test_main(int argc, char *argv[], void *userdata) {
 
         ORDERED_HASHMAP_FOREACH_KEY(val, cmd, event->run_list) {
                 char program[UDEV_PATH_SIZE];
+                bool truncated;
 
-                (void) udev_event_apply_format(event, cmd, program, sizeof(program), false);
+                (void) udev_event_apply_format(event, cmd, program, sizeof(program), false, &truncated);
+                if (truncated)
+                        log_warning("The command '%s' is truncated while substituting into '%s'.", program, cmd);
                 printf("run: '%s'\n", program);
         }