From: Yu Watanabe Date: Mon, 13 Dec 2021 23:05:41 +0000 (+0900) Subject: udev: warn when result of string substitution is truncated X-Git-Tag: v251-rc1~647^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f6caab8995a27244db185f075e751a119e4bdedc;p=thirdparty%2Fsystemd.git udev: warn when result of string substitution is truncated --- diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index a60e4f294c8..3cb83023255 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -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; } diff --git a/src/udev/udev-event.h b/src/udev/udev-event.h index 2067909fde4..823055ddb23 100644 --- a/src/udev/udev-event.h +++ b/src/udev/udev-event.h @@ -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, diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 1a384d6b384..b353b5b8106 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -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) diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c index 01057e12563..8adebbc83e2 100644 --- a/src/udev/udevadm-test.c +++ b/src/udev/udevadm-test.c @@ -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); }