diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-12-25 18:02:32 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-25 18:02:32 +0900 |
commit | 1de6d117effe8e2ee24644894c15d2e57d31632e (patch) | |
tree | b0e4c2b2a3fce09fa06cb6104bd86349c7431aa5 | |
parent | 8585b7ca65e5c5d101a935b41cd081963f790946 (diff) | |
parent | b0c01c9846f29678e4a85da0fa362aeab3aff1d8 (diff) | |
download | systemd-1de6d117effe8e2ee24644894c15d2e57d31632e.tar.gz |
Merge pull request #21765 from yuwata/udev-warn-truncation
udev: warn string truncation
-rw-r--r-- | src/basic/strxcpyx.c | 67 | ||||
-rw-r--r-- | src/basic/strxcpyx.h | 33 | ||||
-rw-r--r-- | src/test/test-strxcpyx.c | 105 | ||||
-rw-r--r-- | src/udev/test-udev-event.c | 23 | ||||
-rw-r--r-- | src/udev/udev-event.c | 119 | ||||
-rw-r--r-- | src/udev/udev-event.h | 6 | ||||
-rw-r--r-- | src/udev/udev-rules.c | 254 | ||||
-rw-r--r-- | src/udev/udevadm-test.c | 5 |
8 files changed, 485 insertions, 127 deletions
diff --git a/src/basic/strxcpyx.c b/src/basic/strxcpyx.c index dbbf7d08d2..52b95650cf 100644 --- a/src/basic/strxcpyx.c +++ b/src/basic/strxcpyx.c @@ -15,57 +15,73 @@ #include <stdio.h> #include <string.h> +#include "string-util.h" #include "strxcpyx.h" -size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) { +size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated) { + bool truncated = false; + assert(dest); assert(src); - if (size == 0) + if (size == 0) { + if (ret_truncated) + *ret_truncated = len > 0; return 0; + } if (len >= size) { if (size > 1) *dest = mempcpy(*dest, src, size-1); size = 0; + truncated = true; } else if (len > 0) { *dest = mempcpy(*dest, src, len); size -= len; } + if (ret_truncated) + *ret_truncated = truncated; + *dest[0] = '\0'; return size; } -size_t strpcpy(char **dest, size_t size, const char *src) { +size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated) { assert(dest); assert(src); - return strnpcpy(dest, size, src, strlen(src)); + return strnpcpy_full(dest, size, src, strlen(src), ret_truncated); } -size_t strpcpyf(char **dest, size_t size, const char *src, ...) { +size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) { + bool truncated = false; va_list va; int i; assert(dest); assert(src); - if (size == 0) - return 0; - va_start(va, src); i = vsnprintf(*dest, size, src, va); - if (i < (int)size) { + va_end(va); + + if (i < (int) size) { *dest += i; size -= i; - } else + } else { size = 0; - va_end(va); + truncated = i > 0; + } + + if (ret_truncated) + *ret_truncated = truncated; + return size; } -size_t strpcpyl(char **dest, size_t size, const char *src, ...) { +size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) { + bool truncated = false; va_list va; assert(dest); @@ -73,31 +89,38 @@ size_t strpcpyl(char **dest, size_t size, const char *src, ...) { va_start(va, src); do { - size = strpcpy(dest, size, src); + bool t; + + size = strpcpy_full(dest, size, src, &t); + truncated = truncated || t; src = va_arg(va, char *); } while (src); va_end(va); + + if (ret_truncated) + *ret_truncated = truncated; return size; } -size_t strnscpy(char *dest, size_t size, const char *src, size_t len) { +size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated) { char *s; assert(dest); assert(src); s = dest; - return strnpcpy(&s, size, src, len); + return strnpcpy_full(&s, size, src, len, ret_truncated); } -size_t strscpy(char *dest, size_t size, const char *src) { +size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated) { assert(dest); assert(src); - return strnscpy(dest, size, src, strlen(src)); + return strnscpy_full(dest, size, src, strlen(src), ret_truncated); } -size_t strscpyl(char *dest, size_t size, const char *src, ...) { +size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) { + bool truncated = false; va_list va; char *s; @@ -107,10 +130,16 @@ size_t strscpyl(char *dest, size_t size, const char *src, ...) { va_start(va, src); s = dest; do { - size = strpcpy(&s, size, src); + bool t; + + size = strpcpy_full(&s, size, src, &t); + truncated = truncated || t; src = va_arg(va, char *); } while (src); va_end(va); + if (ret_truncated) + *ret_truncated = truncated; + return size; } diff --git a/src/basic/strxcpyx.h b/src/basic/strxcpyx.h index cdef492db1..4a648ed033 100644 --- a/src/basic/strxcpyx.h +++ b/src/basic/strxcpyx.h @@ -1,14 +1,33 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include <stdbool.h> #include <stddef.h> #include "macro.h" -size_t strnpcpy(char **dest, size_t size, const char *src, size_t len); -size_t strpcpy(char **dest, size_t size, const char *src); -size_t strpcpyf(char **dest, size_t size, const char *src, ...) _printf_(3, 4); -size_t strpcpyl(char **dest, size_t size, const char *src, ...) _sentinel_; -size_t strnscpy(char *dest, size_t size, const char *src, size_t len); -size_t strscpy(char *dest, size_t size, const char *src); -size_t strscpyl(char *dest, size_t size, const char *src, ...) _sentinel_; +size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated); +static inline size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) { + return strnpcpy_full(dest, size, src, len, NULL); +} +size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated); +static inline size_t strpcpy(char **dest, size_t size, const char *src) { + return strpcpy_full(dest, size, src, NULL); +} +size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _printf_(4, 5); +#define strpcpyf(dest, size, src, ...) \ + strpcpyf_full((dest), (size), NULL, (src), ##__VA_ARGS__) +size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_; +#define strpcpyl(dest, size, src, ...) \ + strpcpyl_full((dest), (size), NULL, (src), ##__VA_ARGS__) +size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated); +static inline size_t strnscpy(char *dest, size_t size, const char *src, size_t len) { + return strnscpy_full(dest, size, src, len, NULL); +} +size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated); +static inline size_t strscpy(char *dest, size_t size, const char *src) { + return strscpy_full(dest, size, src, NULL); +} +size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_; +#define strscpyl(dest, size, src, ...) \ + strscpyl_full(dest, size, NULL, src, ##__VA_ARGS__) diff --git a/src/test/test-strxcpyx.c b/src/test/test-strxcpyx.c index 4e2118979f..dd8dbdea61 100644 --- a/src/test/test-strxcpyx.c +++ b/src/test/test-strxcpyx.c @@ -11,34 +11,86 @@ TEST(strpcpy) { char target[25]; char *s = target; size_t space_left; + bool truncated; space_left = sizeof(target); - space_left = strpcpy(&s, space_left, "12345"); - space_left = strpcpy(&s, space_left, "hey hey hey"); - space_left = strpcpy(&s, space_left, "waldo"); - space_left = strpcpy(&s, space_left, "ba"); - space_left = strpcpy(&s, space_left, "r"); - space_left = strpcpy(&s, space_left, "foo"); + space_left = strpcpy_full(&s, space_left, "12345", &truncated); + assert_se(!truncated); + space_left = strpcpy_full(&s, space_left, "hey hey hey", &truncated); + assert_se(!truncated); + space_left = strpcpy_full(&s, space_left, "waldo", &truncated); + assert_se(!truncated); + space_left = strpcpy_full(&s, space_left, "ba", &truncated); + assert_se(!truncated); + space_left = strpcpy_full(&s, space_left, "r", &truncated); + assert_se(!truncated); + assert_se(space_left == 1); + assert_se(streq(target, "12345hey hey heywaldobar")); + space_left = strpcpy_full(&s, space_left, "", &truncated); + assert_se(!truncated); + assert_se(space_left == 1); assert_se(streq(target, "12345hey hey heywaldobar")); + + space_left = strpcpy_full(&s, space_left, "f", &truncated); + assert_se(truncated); assert_se(space_left == 0); + assert_se(streq(target, "12345hey hey heywaldobar")); + + space_left = strpcpy_full(&s, space_left, "", &truncated); + assert_se(!truncated); + assert_se(space_left == 0); + assert_se(streq(target, "12345hey hey heywaldobar")); + + space_left = strpcpy_full(&s, space_left, "foo", &truncated); + assert_se(truncated); + assert_se(space_left == 0); + assert_se(streq(target, "12345hey hey heywaldobar")); } TEST(strpcpyf) { char target[25]; char *s = target; size_t space_left; + bool truncated; space_left = sizeof(target); - space_left = strpcpyf(&s, space_left, "space left: %zu. ", space_left); - space_left = strpcpyf(&s, space_left, "foo%s", "bar"); - - assert_se(streq(target, "space left: 25. foobar")); + space_left = strpcpyf_full(&s, space_left, &truncated, "space left: %zu. ", space_left); + assert_se(!truncated); + space_left = strpcpyf_full(&s, space_left, &truncated, "foo%s", "bar"); + assert_se(!truncated); assert_se(space_left == 3); + assert_se(streq(target, "space left: 25. foobar")); + + space_left = strpcpyf_full(&s, space_left, &truncated, "%i", 42); + assert_se(!truncated); + assert_se(space_left == 1); + assert_se(streq(target, "space left: 25. foobar42")); + + space_left = strpcpyf_full(&s, space_left, &truncated, "%s", ""); + assert_se(!truncated); + assert_se(space_left == 1); + assert_se(streq(target, "space left: 25. foobar42")); + + space_left = strpcpyf_full(&s, space_left, &truncated, "%c", 'x'); + assert_se(truncated); + assert_se(space_left == 0); + assert_se(streq(target, "space left: 25. foobar42")); + + space_left = strpcpyf_full(&s, space_left, &truncated, "%s", ""); + assert_se(!truncated); + assert_se(space_left == 0); + assert_se(streq(target, "space left: 25. foobar42")); + + space_left = strpcpyf_full(&s, space_left, &truncated, "abc%s", "hoge"); + assert_se(truncated); + assert_se(space_left == 0); + assert_se(streq(target, "space left: 25. foobar42")); /* test overflow */ s = target; - space_left = strpcpyf(&s, 12, "00 left: %i. ", 999); + space_left = strpcpyf_full(&s, 12, &truncated, "00 left: %i. ", 999); + assert_se(truncated); assert_se(streq(target, "00 left: 99")); assert_se(space_left == 0); assert_se(target[12] == '2'); @@ -48,21 +100,40 @@ TEST(strpcpyl) { char target[25]; char *s = target; size_t space_left; + bool truncated; space_left = sizeof(target); - space_left = strpcpyl(&s, space_left, "waldo", " test", " waldo. ", NULL); - space_left = strpcpyl(&s, space_left, "Banana", NULL); - + space_left = strpcpyl_full(&s, space_left, &truncated, "waldo", " test", " waldo. ", NULL); + assert_se(!truncated); + space_left = strpcpyl_full(&s, space_left, &truncated, "Banana", NULL); + assert_se(!truncated); + assert_se(space_left == 1); assert_se(streq(target, "waldo test waldo. Banana")); + + space_left = strpcpyl_full(&s, space_left, &truncated, "", "", "", NULL); + assert_se(!truncated); assert_se(space_left == 1); + assert_se(streq(target, "waldo test waldo. Banana")); + + space_left = strpcpyl_full(&s, space_left, &truncated, "", "x", "", NULL); + assert_se(truncated); + assert_se(space_left == 0); + assert_se(streq(target, "waldo test waldo. Banana")); + + space_left = strpcpyl_full(&s, space_left, &truncated, "hoge", NULL); + assert_se(truncated); + assert_se(space_left == 0); + assert_se(streq(target, "waldo test waldo. Banana")); } TEST(strscpy) { char target[25]; size_t space_left; + bool truncated; space_left = sizeof(target); - space_left = strscpy(target, space_left, "12345"); + space_left = strscpy_full(target, space_left, "12345", &truncated); + assert_se(!truncated); assert_se(streq(target, "12345")); assert_se(space_left == 20); @@ -71,9 +142,11 @@ TEST(strscpy) { TEST(strscpyl) { char target[25]; size_t space_left; + bool truncated; space_left = sizeof(target); - space_left = strscpyl(target, space_left, "12345", "waldo", "waldo", NULL); + space_left = strscpyl_full(target, space_left, &truncated, "12345", "waldo", "waldo", NULL); + assert_se(!truncated); assert_se(streq(target, "12345waldowaldo")); assert_se(space_left == 10); diff --git a/src/udev/test-udev-event.c b/src/udev/test-udev-event.c index b1a631dea2..b6b2c91b2e 100644 --- a/src/udev/test-udev-event.c +++ b/src/udev/test-udev-event.c @@ -9,7 +9,7 @@ #define BUF_SIZE 1024 -static void test_event_spawn_core(bool with_pidfd, const char *cmd, char result_buf[BUF_SIZE]) { +static void test_event_spawn_core(bool with_pidfd, const char *cmd, char *result_buf, size_t buf_size) { _cleanup_(sd_device_unrefp) sd_device *dev = NULL; _cleanup_(udev_event_freep) UdevEvent *event = NULL; @@ -17,12 +17,12 @@ static void test_event_spawn_core(bool with_pidfd, const char *cmd, char result_ assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net/lo") >= 0); assert_se(event = udev_event_new(dev, 0, NULL, LOG_DEBUG)); - assert_se(udev_event_spawn(event, 5 * USEC_PER_SEC, SIGKILL, false, cmd, result_buf, BUF_SIZE) == 0); + assert_se(udev_event_spawn(event, 5 * USEC_PER_SEC, SIGKILL, false, cmd, result_buf, buf_size, NULL) == 0); assert_se(unsetenv("SYSTEMD_PIDFD") >= 0); } -static void test_event_spawn_cat(bool with_pidfd) { +static void test_event_spawn_cat(bool with_pidfd, size_t buf_size) { _cleanup_strv_free_ char **lines = NULL; _cleanup_free_ char *cmd = NULL; char result_buf[BUF_SIZE]; @@ -32,13 +32,16 @@ static void test_event_spawn_cat(bool with_pidfd) { assert_se(find_executable("cat", &cmd) >= 0); assert_se(strextend_with_separator(&cmd, " ", "/sys/class/net/lo/uevent")); - test_event_spawn_core(with_pidfd, cmd, result_buf); + test_event_spawn_core(with_pidfd, cmd, result_buf, + buf_size >= BUF_SIZE ? BUF_SIZE : buf_size); assert_se(lines = strv_split_newlines(result_buf)); strv_print(lines); - assert_se(strv_contains(lines, "INTERFACE=lo")); - assert_se(strv_contains(lines, "IFINDEX=1")); + if (buf_size >= BUF_SIZE) { + assert_se(strv_contains(lines, "INTERFACE=lo")); + assert_se(strv_contains(lines, "IFINDEX=1")); + } } static void test_event_spawn_self(const char *self, const char *arg, bool with_pidfd) { @@ -50,7 +53,7 @@ static void test_event_spawn_self(const char *self, const char *arg, bool with_p assert_se(cmd = strjoin(self, " ", arg)); - test_event_spawn_core(with_pidfd, cmd, result_buf); + test_event_spawn_core(with_pidfd, cmd, result_buf, BUF_SIZE); assert_se(lines = strv_split_newlines(result_buf)); strv_print(lines); @@ -92,8 +95,10 @@ int main(int argc, char *argv[]) { assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); - test_event_spawn_cat(true); - test_event_spawn_cat(false); + test_event_spawn_cat(true, SIZE_MAX); + test_event_spawn_cat(false, SIZE_MAX); + test_event_spawn_cat(true, 5); + test_event_spawn_cat(false, 5); assert_se(path_make_absolute_cwd(argv[0], &self) >= 0); path_simplify(self); diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index a60e4f294c..aa7d229816 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -50,6 +50,7 @@ typedef struct Spawn { char *result; size_t result_size; size_t result_len; + bool truncated; } Spawn; UdevEvent *udev_event_new(sd_device *dev, usec_t exec_delay_usec, sd_netlink *rtnl, int log_level) { @@ -237,9 +238,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 +252,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 +266,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 +274,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 +284,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 +293,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 +312,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 +334,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 +346,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 +368,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 +388,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 +396,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 +436,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 +477,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 +502,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 +515,10 @@ size_t udev_event_apply_format(UdevEvent *event, } assert(size >= 1); + + if (ret_truncated) + *ret_truncated = truncated; + *dest = '\0'; return size; } @@ -555,7 +586,7 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd size = sizeof(buf); } - l = read(fd, p, size - 1); + l = read(fd, p, size - (p == buf)); if (l < 0) { if (errno == EAGAIN) goto reenable; @@ -566,6 +597,13 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd return 0; } + if ((size_t) l == size) { + log_device_warning(spawn->device, "Truncating stdout of '%s' up to %zu byte.", + spawn->cmd, spawn->result_size); + l--; + spawn->truncated = true; + } + p[l] = '\0'; if (fd == spawn->fd_stdout && spawn->result) spawn->result_len += l; @@ -586,7 +624,7 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd fd == spawn->fd_stdout ? "out" : "err", *q); } - if (l == 0) + if (l == 0 || spawn->truncated) return 0; reenable: @@ -725,12 +763,16 @@ static int spawn_wait(Spawn *spawn) { return sd_event_loop(e); } -int udev_event_spawn(UdevEvent *event, - usec_t timeout_usec, - int timeout_signal, - bool accept_failure, - const char *cmd, - char *result, size_t ressize) { +int udev_event_spawn( + UdevEvent *event, + usec_t timeout_usec, + int timeout_signal, + bool accept_failure, + const char *cmd, + char *result, + size_t ressize, + bool *ret_truncated) { + _cleanup_close_pair_ int outpipe[2] = {-1, -1}, errpipe[2] = {-1, -1}; _cleanup_strv_free_ char **argv = NULL; char **envp = NULL; @@ -821,6 +863,9 @@ int udev_event_spawn(UdevEvent *event, if (result) result[spawn.result_len] = '\0'; + if (ret_truncated) + *ret_truncated = spawn.truncated; + return r; /* 0 for success, and positive if the program failed */ } @@ -1095,7 +1140,7 @@ void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_s log_device_debug(event->dev, "Running command \"%s\"", command); - r = udev_event_spawn(event, timeout_usec, timeout_signal, false, command, NULL, 0); + r = udev_event_spawn(event, timeout_usec, timeout_signal, false, command, NULL, 0, NULL); if (r < 0) log_device_warning_errno(event->dev, r, "Failed to execute '%s', ignoring: %m", command); else if (r > 0) /* returned value is positive when program fails */ diff --git a/src/udev/udev-event.h b/src/udev/udev-event.h index 2067909fde..d201fb580a 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, @@ -65,7 +66,8 @@ int udev_event_spawn( bool accept_failure, const char *cmd, char *result, - size_t ressize); + size_t ressize, + bool *ret_truncated); int udev_event_execute_rules( UdevEvent *event, int inotify_fd, diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 1a384d6b38..836a7789fb 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,13 +1727,20 @@ 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)); + r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof(result), NULL); if (r != 0) { if (r < 0) log_rule_warning_errno(dev, rules, r, "Failed to execute \"%s\": %m", buf); @@ -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,11 +1815,18 @@ 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); + r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof result, &truncated); if (r != 0) { if (r < 0) log_rule_warning_errno(dev, rules, r, "Failed to execute '%s', ignoring: %m", buf); @@ -1781,10 +1835,26 @@ static int udev_rule_apply_token_to_event( return token->op == OP_NOMATCH; } + if (truncated) { + bool found = false; + + /* Drop the last line. */ + for (char *p = buf + strlen(buf) - 1; p >= buf; p--) + if (strchr(NEWLINE, *p)) { + *p = '\0'; + found = true; + } else if (found) + break; + } + r = strv_split_newlines_full(&lines, result, EXTRACT_RETAIN_ESCAPE); - if (r < 0) + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { log_rule_warning_errno(dev, rules, r, "Failed to extract lines from result of command \"%s\", ignoring: %m", buf); + return false; + } STRV_FOREACH(line, lines) { char *key, *value; @@ -1813,6 +1883,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 +1897,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 +1952,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 +2009,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 +2033,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 +2056,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 +2110,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 +2149,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 +2161,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 +2191,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 +2219,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 +2234,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 +2259,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 +2272,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 +2299,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 +2315,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 +2352,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 +2379,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 +2389,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 01057e1256..8adebbc83e 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); } |