mirror of
https://github.com/systemd/systemd.git
synced 2025-03-21 02:50:18 +03:00
Merge pull request #21765 from yuwata/udev-warn-truncation
udev: warn string truncation
This commit is contained in:
commit
1de6d117ef
@ -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;
|
||||
}
|
||||
|
@ -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__)
|
||||
|
@ -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);
|
||||
|
||||
assert_se(streq(target, "waldo test waldo. Banana"));
|
||||
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);
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user