summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.github/workflows/build_test.sh7
-rw-r--r--hwdb.d/60-keyboard.hwdb8
-rw-r--r--meson.build24
-rw-r--r--meson_options.txt2
-rw-r--r--src/basic/missing_syscall.h3
-rw-r--r--src/basic/random-util.c16
-rw-r--r--src/basic/strxcpyx.c67
-rw-r--r--src/basic/strxcpyx.h33
-rw-r--r--src/boot/efi/console.c2
-rw-r--r--src/core/meson.build14
-rw-r--r--src/core/service.c4
-rw-r--r--src/network/networkd-setlink.c2
-rw-r--r--src/shared/meson.build4
-rw-r--r--src/test/meson.build3
-rw-r--r--src/test/test-strxcpyx.c105
-rw-r--r--src/udev/test-udev-event.c23
-rw-r--r--src/udev/udev-event.c119
-rw-r--r--src/udev/udev-event.h6
-rw-r--r--src/udev/udev-rules.c254
-rw-r--r--src/udev/udevadm-test.c5
-rw-r--r--test/test-functions14
-rwxr-xr-xtest/units/testsuite-68.sh2
-rwxr-xr-xtools/oss-fuzz.sh4
23 files changed, 550 insertions, 171 deletions
diff --git a/.github/workflows/build_test.sh b/.github/workflows/build_test.sh
index 713a413bf9..78bc35a43b 100755
--- a/.github/workflows/build_test.sh
+++ b/.github/workflows/build_test.sh
@@ -80,9 +80,10 @@ if [[ "$COMPILER" == clang ]]; then
# llvm package if available in such cases to avoid that.
if ! apt show --quiet "llvm-$COMPILER_VERSION" &>/dev/null; then
# Latest LLVM stack deb packages provided by https://apt.llvm.org/
- # Following snippet was borrowed from https://apt.llvm.org/llvm.sh
- wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
- add-apt-repository -y "deb http://apt.llvm.org/$RELEASE/ llvm-toolchain-$RELEASE-$COMPILER_VERSION main"
+ # Following snippet was partly borrowed from https://apt.llvm.org/llvm.sh
+ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | gpg --yes --dearmor --output /usr/share/keyrings/apt-llvm-org.gpg
+ printf "deb [signed-by=/usr/share/keyrings/apt-llvm-org.gpg] http://apt.llvm.org/%s/ llvm-toolchain-%s-%s main\n" \
+ "$RELEASE" "$RELEASE" "$COMPILER_VERSION" >/etc/apt/sources.list.d/llvm-toolchain.list
PACKAGES+=("clang-$COMPILER_VERSION" "lldb-$COMPILER_VERSION" "lld-$COMPILER_VERSION" "clangd-$COMPILER_VERSION")
fi
elif [[ "$COMPILER" == gcc ]]; then
diff --git a/hwdb.d/60-keyboard.hwdb b/hwdb.d/60-keyboard.hwdb
index 2c1cdebc35..4b07b89e9b 100644
--- a/hwdb.d/60-keyboard.hwdb
+++ b/hwdb.d/60-keyboard.hwdb
@@ -231,6 +231,14 @@ evdev:name:Asus Laptop extra buttons:dmi:bvn*:bvr*:bd*:svnASUS*:pn*:*
evdev:input:b0003v0B05p1869*
KEYBOARD_KEY_ff31007c=f20 # Remap micmute to f20
+# Asus TF103C misses the home button in its PNP0C40 GPIO resources
+# causing the volume-button mappings to be off by one, correct this
+evdev:name:gpio-keys:phys:gpio-keys/input0:ev:3:dmi:*:svnASUSTeKCOMPUTERINC.:pnTF103C*:*
+ KEYBOARD_KEY_1=volumeup
+
+evdev:name:gpio-keys:phys:gpio-keys/input0:ev:100003:dmi:*:svnASUSTeKCOMPUTERINC.:pnTF103C*:*
+ KEYBOARD_KEY_0=volumedown
+
###########################################################
# BenQ
###########################################################
diff --git a/meson.build b/meson.build
index 2665024426..17b48d2af4 100644
--- a/meson.build
+++ b/meson.build
@@ -599,6 +599,11 @@ endif
versiondep = declare_dependency(sources: version_h)
+shared_lib_tag = get_option('shared-lib-tag')
+if shared_lib_tag == ''
+ shared_lib_tag = meson.project_version()
+endif
+
sh = find_program('sh')
echo = find_program('echo')
sed = find_program('sed')
@@ -1001,11 +1006,11 @@ else
# Support 'versioned' clang/llvm-strip binaries, as seen on Debian/Ubuntu
# (like clang-10/llvm-strip-10)
clang_bin = cc.get_id() == 'clang' ? cc.cmd_array()[0] : 'clang'
- if clang_bin.contains('afl-clang') or clang_bin.contains('hfuzz-clang')
+ if meson.is_cross_build() or clang_bin.contains('afl-clang') or clang_bin.contains('hfuzz-clang')
clang_bin = 'clang'
endif
clang = find_program(clang_bin, required : bpf_framework_required)
- if clang.found()
+ if not meson.is_cross_build() and clang.found()
llvm_strip_bin = run_command(clang, '--print-prog-name', 'llvm-strip',
check : true).stdout().strip()
else
@@ -2021,12 +2026,7 @@ dbus_programs += executable(
link_with : [libcore,
libshared],
dependencies : [versiondep,
- threads,
- librt,
- libseccomp,
- libselinux,
- libmount,
- libblkid],
+ libseccomp],
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
@@ -2042,12 +2042,7 @@ public_programs += executable(
link_with : [libcore,
libshared],
dependencies : [versiondep,
- threads,
- librt,
- libseccomp,
- libselinux,
- libmount,
- libblkid],
+ libseccomp],
install_rpath : rootlibexecdir,
install : conf.get('ENABLE_ANALYZE'))
@@ -3912,6 +3907,7 @@ summary({
'D-Bus system directory' : dbussystemservicedir,
'bash completions directory' : bashcompletiondir,
'zsh completions directory' : zshcompletiondir,
+ 'private shared lib version tag' : shared_lib_tag,
'extra start script' : get_option('rc-local'),
'debug shell' : '@0@ @ @1@'.format(get_option('debug-shell'),
get_option('debug-tty')),
diff --git a/meson_options.txt b/meson_options.txt
index 401f0933d7..23691fb498 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -3,6 +3,8 @@
option('version-tag', type : 'string',
description : 'override the git version string')
+option('shared-lib-tag', type : 'string',
+ description : 'override the private shared library version tag (defaults to project version)')
option('mode', type : 'combo', choices : ['developer', 'release'],
description : 'autoenable features suitable for systemd development/release builds')
diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h
index 0b0cc3cec2..8267b1a90c 100644
--- a/src/basic/missing_syscall.h
+++ b/src/basic/missing_syscall.h
@@ -78,7 +78,8 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) {
/* ======================================================================= */
#if !HAVE_GETRANDOM
-static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) {
+/* glibc says getrandom() returns ssize_t */
+static inline ssize_t missing_getrandom(void *buffer, size_t count, unsigned flags) {
# ifdef __NR_getrandom
return syscall(__NR_getrandom, buffer, count, flags);
# else
diff --git a/src/basic/random-util.c b/src/basic/random-util.c
index c2be962355..e117330857 100644
--- a/src/basic/random-util.c
+++ b/src/basic/random-util.c
@@ -161,7 +161,6 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
static int have_syscall = -1;
_cleanup_close_ int fd = -1;
bool got_some = false;
- int r;
/* Gathers some high-quality randomness from the kernel (or potentially mid-quality randomness from
* the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't block, unless the RANDOM_BLOCK
@@ -220,18 +219,19 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) {
for (;;) {
- r = getrandom(p, n,
+ ssize_t l;
+ l = getrandom(p, n,
(FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) |
(FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0));
- if (r > 0) {
+ if (l > 0) {
have_syscall = true;
- if ((size_t) r == n)
+ if ((size_t) l == n)
return 0; /* Yay, success! */
- assert((size_t) r < n);
- p = (uint8_t*) p + r;
- n -= r;
+ assert((size_t) l < n);
+ p = (uint8_t*) p + l;
+ n -= l;
if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
/* Fill in the remaining bytes using pseudo-random values */
@@ -248,7 +248,7 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
/* Fill in the rest with /dev/urandom */
break;
- } else if (r == 0) {
+ } else if (l == 0) {
have_syscall = true;
return -EIO;
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/boot/efi/console.c b/src/boot/efi/console.c
index 89fbd94258..86cd15a235 100644
--- a/src/boot/efi/console.c
+++ b/src/boot/efi/console.c
@@ -32,7 +32,7 @@ static inline void EventClosep(EFI_EVENT *event) {
* Also, multiple input protocols can be backed by the same device, but they can be out of
* sync. Falling back on a different protocol can end up with double input.
*
- * Therefore, we will perferrably use TextInputEx for ConIn if that is available. Additionally,
+ * Therefore, we will preferably use TextInputEx for ConIn if that is available. Additionally,
* we look for the first TextInputEx device the firmware gives us as a fallback option. It
* will replace ConInEx permanently if it ever reports a key press.
* Lastly, a timer event allows us to provide a input timeout without having to call into
diff --git a/src/core/meson.build b/src/core/meson.build
index d229d46779..fd15a7fbef 100644
--- a/src/core/meson.build
+++ b/src/core/meson.build
@@ -167,12 +167,18 @@ load_fragment_gperf_nulstr_c = custom_target(
command : [awk, '-f', '@INPUT0@', '@INPUT1@'],
capture : true)
-libcore = static_library(
- 'core',
+libcore_name = 'systemd-core-@0@'.format(shared_lib_tag)
+
+libcore = shared_library(
+ libcore_name,
libcore_sources,
load_fragment_gperf_c,
load_fragment_gperf_nulstr_c,
include_directories : includes,
+ c_args : ['-fvisibility=default'],
+ link_args : ['-shared',
+ '-Wl,--version-script=' + libshared_sym_path],
+ link_with : libshared,
dependencies : [versiondep,
threads,
libdl,
@@ -184,8 +190,10 @@ libcore = static_library(
libapparmor,
libselinux,
libmount,
+ libblkid,
libacl],
- build_by_default : false)
+ install : true,
+ install_dir : rootlibexecdir)
core_includes = [includes, include_directories('.')]
diff --git a/src/core/service.c b/src/core/service.c
index b8430eae96..88307fa1a5 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1468,8 +1468,8 @@ static int service_create_monitor_md_env(Job *j, char **ret) {
*
* For example:
*
- * MONITOR_METADATA="SERVICE_RESULT=exit-code,EXIT_CODE=exited,EXIT_STATUS=1,INVOCATION_ID=02dd868af2f344b18edaf74b618b2f90,UNIT=failer.service;
- * SERVICE_RESULT=exit-code,EXIT_CODE=exited,EXIT_STATUS=1,INVOCATION_ID=80cb228bd7344f77a090eda603a3cfe2,UNIT=failer2.service"
+ * MONITOR_METADATA="SERVICE_RESULT=exit-code,EXIT_CODE=exited,EXIT_STATUS=1,INVOCATION_ID=02dd868af2f344b18edaf74b618b2f90,UNIT=failure.service;
+ * SERVICE_RESULT=exit-code,EXIT_CODE=exited,EXIT_STATUS=1,INVOCATION_ID=80cb228bd7344f77a090eda603a3cfe2,UNIT=failure2.service"
*/
LIST_FOREACH(triggered_by, tu, j->triggered_by) {
diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c
index 2b659e8f36..b9fd1d749e 100644
--- a/src/network/networkd-setlink.c
+++ b/src/network/networkd-setlink.c
@@ -1173,7 +1173,7 @@ static bool link_is_ready_to_bring_up_or_down(Link *link, bool up) {
if (up && link->dsa_master_ifindex > 0) {
Link *master;
- /* The master inteface must be up. See comments in link_up_dsa_slave(). */
+ /* The master interface must be up. See comments in link_up_dsa_slave(). */
if (link_get_by_index(link->manager, link->dsa_master_ifindex, &master) < 0)
return false;
diff --git a/src/shared/meson.build b/src/shared/meson.build
index 5dc58a863d..f58d623f4a 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -430,7 +430,7 @@ target2 = custom_target(
shared_generated_gperf_headers = [target1, target2]
shared_sources += shared_generated_gperf_headers
-libshared_name = 'systemd-shared-@0@'.format(meson.project_version())
+libshared_name = 'systemd-shared-@0@'.format(shared_lib_tag)
libshared_deps = [threads,
libacl,
@@ -465,13 +465,13 @@ libshared_static = static_library(
libshared = shared_library(
libshared_name,
include_directories : includes,
+ c_args : ['-fvisibility=default'],
link_args : ['-shared',
'-Wl,--version-script=' + libshared_sym_path],
link_whole : [libshared_static,
libbasic,
libbasic_gcrypt,
libsystemd_static],
- c_args : ['-fvisibility=default'],
dependencies : libshared_deps,
install : true,
install_dir : rootlibexecdir)
diff --git a/src/test/meson.build b/src/test/meson.build
index 9a1c481f22..364cd3dd3f 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -419,7 +419,8 @@ tests += [
libmount,
libxz,
liblz4,
- libblkid],
+ libblkid,
+ libselinux],
[core_includes, journal_includes, udev_includes]],
[['src/test/test-prioq.c']],
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..ea4d548f60 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 truncated 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);
}
diff --git a/test/test-functions b/test/test-functions
index 218d0e6888..95d37d09d5 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -1932,11 +1932,9 @@ inst_libs() {
while read -r line; do
[[ "$line" = 'not a dynamic executable' ]] && break
- # Skip a harmless error when running the tests on a system with a significantly
- # older systemd version (ldd tries to resolve the unprefixed RPATH for libsystemd.so.0,
- # which is in this case older than the already installed libsystemd.so.0 in $initdir).
- # The issue is triggered by installing test dependencies in install_missing_libraries().
- [[ "$line" =~ libsystemd.so.*:\ version\ .*\ not\ found ]] && continue
+ # Ignore errors about our own stuff missing. This is most likely caused
+ # by ldd attempting to use the unprefixed RPATH.
+ [[ "$line" =~ libsystemd.*\ not\ found ]] && continue
if [[ "$line" =~ $so_regex ]]; then
file="${BASH_REMATCH[1]}"
@@ -2309,7 +2307,7 @@ inst_binary() {
# In certain cases we might attempt to install a binary which is already
# present in the test image, yet it's missing from the host system.
# In such cases, let's check if the binary indeed exists in the image
- # before doing any other chcecks. If it does, immediately return with
+ # before doing any other checks. If it does, immediately return with
# success.
if [[ $# -eq 1 ]]; then
for path in "" bin sbin usr/bin usr/sbin; do
@@ -2328,6 +2326,10 @@ inst_binary() {
while read -r line; do
[[ "$line" = 'not a dynamic executable' ]] && break
+ # Ignore errors about our own stuff missing. This is most likely caused
+ # by ldd attempting to use the unprefixed RPATH.
+ [[ "$line" =~ libsystemd.*\ not\ found ]] && continue
+
if [[ "$line" =~ $so_regex ]]; then
file="${BASH_REMATCH[1]}"
[[ -e "${initdir}/$file" ]] && continue
diff --git a/test/units/testsuite-68.sh b/test/units/testsuite-68.sh
index 1ac244bc5f..15e2e0353f 100755
--- a/test/units/testsuite-68.sh
+++ b/test/units/testsuite-68.sh
@@ -39,7 +39,7 @@ EOF
# Another service which triggers testservice-failure-exit-handler-68.service
cat >/run/systemd/system/testservice-failure-68-additional.service <<EOF
[Unit]
-Description=TEST-68-PROPAGATE-EXIT-STATUS Additonal service with OnFailure= trigger
+Description=TEST-68-PROPAGATE-EXIT-STATUS Additional service with OnFailure= trigger
OnFailure=testservice-failure-exit-handler-68.service
[Service]
diff --git a/tools/oss-fuzz.sh b/tools/oss-fuzz.sh
index 8a19da665e..244eb83906 100755
--- a/tools/oss-fuzz.sh
+++ b/tools/oss-fuzz.sh
@@ -67,7 +67,9 @@ df="$build/dns-fuzzing"
git clone --depth 1 https://github.com/CZ-NIC/dns-fuzzing "$df"
zip -jqr "$OUT/fuzz-dns-packet_seed_corpus.zip" "$df/packet"
-install -Dt "$OUT/src/shared/" "$build"/src/shared/libsystemd-shared-*.so
+install -Dt "$OUT/src/shared/" \
+ "$build"/src/shared/libsystemd-shared-*.so \
+ "$build"/src/core/libsystemd-core-*.so
wget -O "$OUT/fuzz-json.dict" https://raw.githubusercontent.com/rc0r/afl-fuzz/master/dictionaries/json.dict