diff options
-rwxr-xr-x | .github/workflows/build_test.sh | 7 | ||||
-rw-r--r-- | hwdb.d/60-keyboard.hwdb | 8 | ||||
-rw-r--r-- | meson.build | 24 | ||||
-rw-r--r-- | meson_options.txt | 2 | ||||
-rw-r--r-- | src/basic/missing_syscall.h | 3 | ||||
-rw-r--r-- | src/basic/random-util.c | 16 | ||||
-rw-r--r-- | src/basic/strxcpyx.c | 67 | ||||
-rw-r--r-- | src/basic/strxcpyx.h | 33 | ||||
-rw-r--r-- | src/boot/efi/console.c | 2 | ||||
-rw-r--r-- | src/core/meson.build | 14 | ||||
-rw-r--r-- | src/core/service.c | 4 | ||||
-rw-r--r-- | src/network/networkd-setlink.c | 2 | ||||
-rw-r--r-- | src/shared/meson.build | 4 | ||||
-rw-r--r-- | src/test/meson.build | 3 | ||||
-rw-r--r-- | src/test/test-strxcpyx.c | 105 | ||||
-rw-r--r-- | src/udev/test-udev-event.c | 23 | ||||
-rw-r--r-- | src/udev/udev-event.c | 119 | ||||
-rw-r--r-- | src/udev/udev-event.h | 6 | ||||
-rw-r--r-- | src/udev/udev-rules.c | 254 | ||||
-rw-r--r-- | src/udev/udevadm-test.c | 5 | ||||
-rw-r--r-- | test/test-functions | 14 | ||||
-rwxr-xr-x | test/units/testsuite-68.sh | 2 | ||||
-rwxr-xr-x | tools/oss-fuzz.sh | 4 |
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 |