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;
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);
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)
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)
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: {
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: {
}
if (index == 0)
- strpcpy(&s, l, event->program_result);
+ strpcpy_full(&s, l, event->program_result, &truncated);
else {
const char *start, *p;
unsigned i;
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;
case FORMAT_SUBST_ATTR: {
char vbuf[UDEV_NAME_SIZE];
int count;
+ bool t;
if (isempty(attr))
return -EINVAL;
/* 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:
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);
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))
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;
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",
break;
}
+ truncated = truncated || t;
+
/* FORMAT_SUBST_RESULT handles spaces itself */
if (replace_whitespace && type != FORMAT_SUBST_RESULT)
/* udev_replace_whitespace can replace in-place,
}
assert(size >= 1);
+
+ if (ret_truncated)
+ *ret_truncated = truncated;
+
*dest = '\0';
return size;
}
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);
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:
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)
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;
}
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);
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];
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);
}
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));
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");
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);
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 */
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);
}
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,
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);
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);
}
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);
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
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)
}
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)
}
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);
}
case TK_A_NAME: {
char buf[UDEV_PATH_SIZE];
+ bool truncated;
size_t count;
if (event->name_final)
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);
}
case TK_A_DEVLINK: {
char buf[UDEV_PATH_SIZE], *p;
+ bool truncated;
size_t count;
if (event->devlink_final)
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)
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);
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,
}
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);
case TK_A_RUN_PROGRAM: {
_cleanup_free_ char *cmd = NULL;
char buf[UDEV_PATH_SIZE];
+ bool truncated;
if (event->run_final)
break;
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)