From 832dd546a9379b41347b2a57fe6287fe63a03a87 Mon Sep 17 00:00:00 2001 From: Jan Macku Date: Tue, 12 May 2026 10:49:28 +0200 Subject: [PATCH] systemd-257-25 Resolves: RHEL-171097, RHEL-155454, RHEL-128058, RHEL-155021, RHEL-72814, RHEL-153030 --- ...t-s-dropin-directories-from-global-c.patch | 42 + ...const-decorators-on-public-inline-fu.patch | 89 ++ ...rop-_sd_const_-from-sd_id128_in_setv.patch | 30 + ...-umount-when-running-with-sanitizers.patch | 57 + ...ore-binaries-when-running-with-sanit.patch | 93 ++ ...-ignore-sanitizer-warning-about-bloc.patch | 71 + ...duce-the-performance-memory-overhead.patch | 37 + ...support-swap-on-network-block-device.patch | 324 ++++ ...n-add-tags-for-the-next-few-versions.patch | 27 + ...est-drop-some-extraneous-whitespaces.patch | 29 + ...ult-when-processing-matchHostname-fi.patch | 52 + ...lude-valid-min-max-values-for-cgroup.patch | 57 + ...-don-t-leak-memory-from-array-fields.patch | 75 + ...ort-option-for-userdbctl-s-from-file.patch | 25 + 0676-fuzz-add-a-fuzzer-for-user-records.patch | 1376 +++++++++++++++++ ...act-user_record_image_is_blockdev-co.patch | 81 + ...introduce-naming-scheme-for-RHEL-9.9.patch | 58 + ...ntroduce-naming-scheme-for-RHEL-10.3.patch | 58 + ...Tag-accel-devices-for-uaccess-render.patch | 28 + ...kfd-devices-for-xaccess-render-40888.patch | 29 + systemd.spec | 44 +- 21 files changed, 2681 insertions(+), 1 deletion(-) create mode 100644 0662-core-cleanup-unit-s-dropin-directories-from-global-c.patch create mode 100644 0663-libsystemd-drop-const-decorators-on-public-inline-fu.patch create mode 100644 0664-sd-id128-Drop-_sd_const_-from-sd_id128_in_setv.patch create mode 100644 0665-test-wrap-mount-umount-when-running-with-sanitizers.patch create mode 100644 0666-test-wrap-even-more-binaries-when-running-with-sanit.patch create mode 100644 0667-test-temporarily-ignore-sanitizer-warning-about-bloc.patch create mode 100644 0668-test-slightly-reduce-the-performance-memory-overhead.patch create mode 100644 0669-fstab-generator-support-swap-on-network-block-device.patch create mode 100644 0670-man-add-tags-for-the-next-few-versions.patch create mode 100644 0671-test-drop-some-extraneous-whitespaces.patch create mode 100644 0672-shared-fix-segfault-when-processing-matchHostname-fi.patch create mode 100644 0673-shared-don-t-exclude-valid-min-max-values-for-cgroup.patch create mode 100644 0674-shared-don-t-leak-memory-from-array-fields.patch create mode 100644 0675-man-fix-short-option-for-userdbctl-s-from-file.patch create mode 100644 0676-fuzz-add-a-fuzzer-for-user-records.patch create mode 100644 0677-user-record-extract-user_record_image_is_blockdev-co.patch create mode 100644 0678-udev-net_id-introduce-naming-scheme-for-RHEL-9.9.patch create mode 100644 0679-udev-net_id-introduce-naming-scheme-for-RHEL-10.3.patch create mode 100644 0680-Tag-accel-devices-for-uaccess-render.patch create mode 100644 0681-udev-tag-kfd-devices-for-xaccess-render-40888.patch diff --git a/0662-core-cleanup-unit-s-dropin-directories-from-global-c.patch b/0662-core-cleanup-unit-s-dropin-directories-from-global-c.patch new file mode 100644 index 0000000..0a17517 --- /dev/null +++ b/0662-core-cleanup-unit-s-dropin-directories-from-global-c.patch @@ -0,0 +1,42 @@ +From 391d1d3b7e0b47749369ea6d590991d19032fdab Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Wed, 25 Feb 2026 19:45:55 +0100 +Subject: [PATCH] core: cleanup unit's dropin directories from global cache + +When user creates dropin files via API (e.g. systemctl set-property ...) +we put the dropin directory path into unit_path_cache. Drop those +directories from the cache in unit_free() and prevent memory leak. + +Follow-up for fce94c5c563b8f6ede2b8f7f283d2d2faff4e062. + +(cherry picked from commit 0c98e432d1def1e8428dbead50dc629ed0645366) + +Resolves: RHEL-171097 +--- + src/core/unit.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/core/unit.c b/src/core/unit.c +index 6b31599972..6a2f552483 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -653,6 +653,8 @@ static void unit_remove_transient(Unit *u) { + if (!u->transient) + return; + ++ const char *dropin_directory = strjoina(u->id, ".d"); ++ + STRV_FOREACH(i, u->dropin_paths) { + _cleanup_free_ char *p = NULL, *pp = NULL; + +@@ -666,6 +668,10 @@ static void unit_remove_transient(Unit *u) { + if (!path_equal(u->manager->lookup_paths.transient, pp)) + continue; + ++ /* Drop the transient drop-in directory also from unit path cache. */ ++ if (path_equal(last_path_component(p), dropin_directory)) ++ free(set_remove(u->manager->unit_path_cache, p)); ++ + (void) unlink(*i); + (void) rmdir(p); + } diff --git a/0663-libsystemd-drop-const-decorators-on-public-inline-fu.patch b/0663-libsystemd-drop-const-decorators-on-public-inline-fu.patch new file mode 100644 index 0000000..2c20084 --- /dev/null +++ b/0663-libsystemd-drop-const-decorators-on-public-inline-fu.patch @@ -0,0 +1,89 @@ +From e6ed6b793155ab22ee920b0cf3db4e1ada8df9c6 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 14 Oct 2025 11:17:27 +0200 +Subject: [PATCH] libsystemd: drop "const" decorators on public inline + functions + +The point of the "const" attribute is to give the compiler hints about +behaviour of functions if it only has the function prototype but no body +around. But inline functions are the ones where the compiler *always* +has the body around, hence the "const" decorator is really just noise: +the compuler can determine the constness on its own, just by looking at +the code. + +Hence, drop the decorators, it's just noise. And a source of errors, as +675fa49f69943b0f009c973ed3d1e90afc1d88b1 has shown. + +Follow-up for: #39289 + +(cherry picked from commit 86d9498c8cf7aa1584dae85f3e1a570e44a81cfc) + +Related: RHEL-155454 +--- + src/systemd/_sd-common.h | 4 ---- + src/systemd/sd-id128.h | 8 ++++---- + src/systemd/sd-json.h | 2 +- + 3 files changed, 5 insertions(+), 9 deletions(-) + +diff --git a/src/systemd/_sd-common.h b/src/systemd/_sd-common.h +index 5792dd8106..dbe9fa035e 100644 +--- a/src/systemd/_sd-common.h ++++ b/src/systemd/_sd-common.h +@@ -45,10 +45,6 @@ typedef void (*_sd_destroy_t)(void *userdata); + # define _sd_pure_ __attribute__((__pure__)) + #endif + +-#ifndef _sd_const_ +-# define _sd_const_ __attribute__((__const__)) +-#endif +- + /* Note that strictly speaking __deprecated__ has been available before GCC 6. However, starting with GCC 6 + * it also works on enum values, which we are interested in. Since this is a developer-facing feature anyway + * (as opposed to build engineer-facing), let's hence conditionalize this to gcc 6, given that the developers +diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h +index 7be690400d..d63e05e71d 100644 +--- a/src/systemd/sd-id128.h ++++ b/src/systemd/sd-id128.h +@@ -117,17 +117,17 @@ int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret); + #define SD_ID128_MAKE_UUID_STR(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ + #a #b #c #d "-" #e #f "-" #g #h "-" #i #j "-" #k #l #m #n #o #p + +-_sd_const_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) { ++static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) { + return a.qwords[0] == b.qwords[0] && a.qwords[1] == b.qwords[1]; + } + + int sd_id128_string_equal(const char *s, sd_id128_t id); + +-_sd_const_ static __inline__ int sd_id128_is_null(sd_id128_t a) { ++static __inline__ int sd_id128_is_null(sd_id128_t a) { + return a.qwords[0] == 0 && a.qwords[1] == 0; + } + +-_sd_const_ static __inline__ int sd_id128_is_allf(sd_id128_t a) { ++static __inline__ int sd_id128_is_allf(sd_id128_t a) { + return a.qwords[0] == UINT64_C(0xFFFFFFFFFFFFFFFF) && a.qwords[1] == UINT64_C(0xFFFFFFFFFFFFFFFF); + } + +@@ -146,7 +146,7 @@ _sd_const_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) { + } + } + +-_sd_const_ static __inline__ int sd_id128_in_set_sentinel(sd_id128_t a, ...) { ++static __inline__ int sd_id128_in_set_sentinel(sd_id128_t a, ...) { + va_list ap; + int r; + +diff --git a/src/systemd/sd-json.h b/src/systemd/sd-json.h +index 33817f2327..ac5a1b13e3 100644 +--- a/src/systemd/sd-json.h ++++ b/src/systemd/sd-json.h +@@ -339,7 +339,7 @@ int sd_json_variant_strv(sd_json_variant *v, char ***ret); + int sd_json_variant_unbase64(sd_json_variant *v, void **ret, size_t *ret_size); + int sd_json_variant_unhex(sd_json_variant *v, void **ret, size_t *ret_size); + +-_sd_const_ static __inline__ int sd_json_format_enabled(sd_json_format_flags_t flags) { ++static __inline__ int sd_json_format_enabled(sd_json_format_flags_t flags) { + return !(flags & SD_JSON_FORMAT_OFF); + } + diff --git a/0664-sd-id128-Drop-_sd_const_-from-sd_id128_in_setv.patch b/0664-sd-id128-Drop-_sd_const_-from-sd_id128_in_setv.patch new file mode 100644 index 0000000..e04a33f --- /dev/null +++ b/0664-sd-id128-Drop-_sd_const_-from-sd_id128_in_setv.patch @@ -0,0 +1,30 @@ +From ee8dd04d1d9759e0266f0802b033936542a469fe Mon Sep 17 00:00:00 2001 +From: Daan De Meyer +Date: Mon, 13 Oct 2025 10:43:16 +0200 +Subject: [PATCH] sd-id128: Drop _sd_const_ from sd_id128_in_setv() + +Both the const and pure attributes disallow modifying input arguments +but sd_id128_in_setv() clearly modifies its ap input argument by iterating +over it with va_arg() so drop the _sd_const_ attribute from +sd_id128_in_setv(). + +(cherry picked from commit 675fa49f69943b0f009c973ed3d1e90afc1d88b1) + +Related: RHEL-155454 +--- + src/systemd/sd-id128.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h +index d63e05e71d..8d4b6aec0a 100644 +--- a/src/systemd/sd-id128.h ++++ b/src/systemd/sd-id128.h +@@ -134,7 +134,7 @@ static __inline__ int sd_id128_is_allf(sd_id128_t a) { + #define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }}) + #define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }}) + +-_sd_const_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) { ++static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) { + for (;;) { + sd_id128_t b = va_arg(ap, sd_id128_t); + diff --git a/0665-test-wrap-mount-umount-when-running-with-sanitizers.patch b/0665-test-wrap-mount-umount-when-running-with-sanitizers.patch new file mode 100644 index 0000000..5db3a55 --- /dev/null +++ b/0665-test-wrap-mount-umount-when-running-with-sanitizers.patch @@ -0,0 +1,57 @@ +From bb4a13f0b861cad6d69b5dd0223412fb8fd4465e Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Wed, 22 Apr 2026 19:12:23 +0200 +Subject: [PATCH] test: wrap mount/umount when running with sanitizers + +On Fedora Rawhide mount/umount is linked against libsystemd, which then +breaks the binaries in sanitizer runs, as we try to run instrumented +code from an uninstrumented binary: + +bash-5.3# ldd /usr/bin/mount + linux-vdso.so.1 (0x00007fa757ef9000) + libmount.so.1 => /lib64/libmount.so.1 (0x00007fa757e84000) + libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fa757e51000) + libc.so.6 => /lib64/libc.so.6 (0x00007fa757c56000) + libblkid.so.1 => /lib64/libblkid.so.1 (0x00007fa757c16000) + libsystemd.so.0 => /lib64/libsystemd.so.0 (0x00007fa757400000) + libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007fa75734f000) + /lib64/ld-linux-x86-64.so.2 (0x00007fa757efb000) + libclang_rt.asan.so => /usr/lib/clang/22/lib/x86_64-redhat-linux-gnu/libclang_rt.asan.so (0x00007fa756800000) + libm.so.6 => /lib64/libm.so.6 (0x00007fa7566e4000) + libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fa7566b7000) + libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fa756400000) +bash-5.3# mount +==458==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD. + +This then breaks the whole machine, as mount is quite essential during +boot. + +Let's just add mount/umount to the list of wrapped binaries to fix this. + +(cherry picked from commit 8030e0b19ef7c0e823d84dd08ad38a2d88e0a230) + +Related: RHEL-155454 +--- + mkosi.sanitizers/mkosi.postinst | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/mkosi.sanitizers/mkosi.postinst b/mkosi.sanitizers/mkosi.postinst +index e83d05c6bc..c984a91a5a 100755 +--- a/mkosi.sanitizers/mkosi.postinst ++++ b/mkosi.sanitizers/mkosi.postinst +@@ -65,6 +65,7 @@ wrap=( + mdadm + mkfs.btrfs + mksquashfs ++ mount + multipath + multipathd + nvme +@@ -78,6 +79,7 @@ wrap=( + su + tar + tgtd ++ umount + useradd + userdel + veritysetup diff --git a/0666-test-wrap-even-more-binaries-when-running-with-sanit.patch b/0666-test-wrap-even-more-binaries-when-running-with-sanit.patch new file mode 100644 index 0000000..82020fb --- /dev/null +++ b/0666-test-wrap-even-more-binaries-when-running-with-sanit.patch @@ -0,0 +1,93 @@ +From 1bc0a05e4e9a6611b00c1e3dcdb9f0b96cb755cb Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Thu, 23 Apr 2026 13:44:59 +0200 +Subject: [PATCH] test: wrap even more binaries when running with sanitizers + +Turns out that the util-linux dep on libsystemd caused more fun than I +originally anticipated: + +$ lddtree /usr/bin/dfuzzer +dfuzzer => /usr/bin/dfuzzer (interpreter => /lib64/ld-linux-x86-64.so.2) + libgio-2.0.so.0 => /lib64/libgio-2.0.so.0 + libgmodule-2.0.so.0 => /lib64/libgmodule-2.0.so.0 + libz.so.1 => /lib64/libz.so.1 + libmount.so.1 => /lib64/libmount.so.1 + libblkid.so.1 => /lib64/libblkid.so.1 + libsystemd.so.0 => /lib64/libsystemd.so.0 + libm.so.6 => /lib64/libm.so.6 + ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 + libselinux.so.1 => /lib64/libselinux.so.1 + libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 +... + +Also, the tpm2 utils now depend on libudev through libcurl -> libssh -> +libfido2 dep chain: + +$ lddtree /usr/bin/tpm2_pcrread +tpm2_pcrread => /usr/bin/tpm2_pcrread (interpreter => /lib64/ld-linux-x86-64.so.2) + ... + libcurl.so.4 => /lib64/libcurl.so.4 + ... + libssh.so.4 => /lib64/libssh.so.4 + libfido2.so.1 => /lib64/libfido2.so.1 + libcbor.so.0.13 => /lib64/libcbor.so.0.13 + libudev.so.1 => /lib64/libudev.so.1 + libgcc_s.so.1 => /lib64/libgcc_s.so.1 +... + +Follow-up for 8030e0b19ef7c0e823d84dd08ad38a2d88e0a230. + +(cherry picked from commit a8400c8f1a61223e4905e2939f8d71be82831c8c) + +Related: RHEL-155454 +--- + mkosi.sanitizers/mkosi.postinst | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/mkosi.sanitizers/mkosi.postinst b/mkosi.sanitizers/mkosi.postinst +index c984a91a5a..95f3f10a47 100755 +--- a/mkosi.sanitizers/mkosi.postinst ++++ b/mkosi.sanitizers/mkosi.postinst +@@ -44,6 +44,7 @@ wrap=( + dbus-broker-launch + dbus-daemon + delv ++ dfuzzer + dhcpd + dig + dnf +@@ -55,17 +56,21 @@ wrap=( + getfacl + id + integritysetup ++ iscsiadm + iscsid + kpartx + logger + login + ls + lsblk ++ lsns + lvm + mdadm + mkfs.btrfs ++ mkfs.ext4 + mksquashfs + mount ++ mountpoint + multipath + multipathd + nvme +@@ -77,8 +82,12 @@ wrap=( + sshd + stat + su ++ swapoff ++ swapon + tar + tgtd ++ # The tpm2 tools (tpm2_readpublic, tpm2_pcrextend, ...) all are symlinks to tpm2 ++ tpm2 + umount + useradd + userdel diff --git a/0667-test-temporarily-ignore-sanitizer-warning-about-bloc.patch b/0667-test-temporarily-ignore-sanitizer-warning-about-bloc.patch new file mode 100644 index 0000000..02c82e1 --- /dev/null +++ b/0667-test-temporarily-ignore-sanitizer-warning-about-bloc.patch @@ -0,0 +1,71 @@ +From 6ab021ea77231db4e983db6ad6263e261731d680 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Thu, 23 Apr 2026 15:11:01 +0200 +Subject: [PATCH] test: temporarily ignore sanitizer warning about blocked + ptrace() + +LLVM 22 introduced an additional check [0] for ptrace() syscall when +invoking sanitizers [0] which currently produces a false-positive +warning when running some of our units under sanitizers: + +[ 47.524680] systemd-timedated[740]: ==740==WARNING: ptrace appears to be blocked (is seccomp enabled?). LeakSanitizer may hang. +[ 47.524680] systemd-timedated[740]: ==740==Child exited with signal 15. +... +[ 1555.734223] systemd-oomd[93]: ==93==WARNING: ptrace appears to be blocked (is seccomp enabled?). LeakSanitizer may hang. +[ 1555.734223] systemd-oomd[93]: ==93==Child exited with signal 15. +... + +It is a false positive because we disable the seccomp filters +system-wide for our units in the sanitizer jobs. + +Now, from what I've seen so far this happens only in +Type=notify(-reload) units that also utilize bus_event_loop_with_idle(). +This, combined with the fact that the ptrace()-check child process from +[0] checks only if the child process was killed by _any_ signal, means +that if the systemd unit exits on its own after becoming idle and then +something sends it SIGTERM (either via explicit `systemctl stop` or +during system shutdown), this SIGTERM might hit the ptrace()-check child +process from the sanitizer handler (as we also send the signal to all +processes in the target cgroup), which the parent process then +mistakenly evaluates as a blocked ptrace() syscall, even though the +check process wasn't killed by SIGSYS. + +I filed this as [1] to the LLVM project, but let's also temporarily +ignore the warning in the sanitizer report processing, as it currently +causes annoying test fails. + +[0] https://github.com/llvm/llvm-project/commit/a708b4bf21d7c2298224cdacf7d424abc3c8fed4 +[1] https://github.com/llvm/llvm-project/issues/193714 + +(cherry picked from commit 445f9805489a575c9b1bc74daa173c4fdf9b1bf7) + +Related: RHEL-155454 +--- + test/integration-test-wrapper.py | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/test/integration-test-wrapper.py b/test/integration-test-wrapper.py +index 3e0cbbb678..064645223e 100755 +--- a/test/integration-test-wrapper.py ++++ b/test/integration-test-wrapper.py +@@ -136,7 +136,19 @@ def process_sanitizer_report(args: argparse.Namespace, journal_file: Path) -> bo + fatal_end = re.compile(r'==[0-9]+==HINT:\s+\w+Sanitizer') + + # 'Standard' errors: +- standard_begin = re.compile(r'([0-9]+: runtime error|==[0-9]+==.+?\w+Sanitizer)') ++ # ++ # TODO: there's currently a bug in LLVM 22 due to which certain systemd ++ # units can throw the following warning: ++ # [ 3366.747202] systemd-oomd[93]: ==93==WARNING: ptrace appears to be blocked (is seccomp enabled?). ++ # LeakSanitizer may hang. ++ # [ 3366.747202] systemd-oomd[93]: ==93==Child exited with signal 15. ++ # ++ # which is then picked up by the following regex and causes the test to ++ # fail. Let's, temporarily, exclude this warning from the regex to mitigate ++ # this. ++ # ++ # See: https://github.com/llvm/llvm-project/issues/193714 ++ standard_begin = re.compile(r'([0-9]+: runtime error|==[0-9]+==(?!WARNING: ptrace).+?\w+Sanitizer)') + standard_end = re.compile(r'SUMMARY:\s+(\w+)Sanitizer') + + # extract COMM diff --git a/0668-test-slightly-reduce-the-performance-memory-overhead.patch b/0668-test-slightly-reduce-the-performance-memory-overhead.patch new file mode 100644 index 0000000..2037b5d --- /dev/null +++ b/0668-test-slightly-reduce-the-performance-memory-overhead.patch @@ -0,0 +1,37 @@ +From 6cb559be4ea76c36fc83abd9aef98b0033ad853e Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Sat, 25 Apr 2026 16:37:34 +0200 +Subject: [PATCH] test: slightly reduce the performance/memory overhead for + wrapped binaries + +Let's drop the quarantine that ASan uses for use-after-free detection, +as it's pointless in wrapped binaries and can consume up to 256 MiB of +memory (with the default configuration). Also, don't keep any stack +traces for allocations & deallocations, which should (slightly) help +with both memory & performance overhead. + +(cherry picked from commit 035ba3ea571bad6772cf3731f6b5379ccb08267f) + +Related: RHEL-155454 +--- + mkosi.sanitizers/mkosi.postinst | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/mkosi.sanitizers/mkosi.postinst b/mkosi.sanitizers/mkosi.postinst +index 95f3f10a47..c1377bba6f 100755 +--- a/mkosi.sanitizers/mkosi.postinst ++++ b/mkosi.sanitizers/mkosi.postinst +@@ -114,8 +114,11 @@ for bin in "${wrap[@]}"; do + # Preload the ASan runtime DSO, otherwise ASAn will complain + export LD_PRELOAD="$ASAN_RT_PATH" + # Disable LSan to speed things up, since we don't care about leak reports +-# from 'external' binaries +-export ASAN_OPTIONS=detect_leaks=$enable_lsan ++# from 'external' binaries. In addition, disable quarantine (for use-after-free ++# detection) and malloc stack frame collection as we don't care about these in ++# 'external' binaries either, and they just unnecessarily hog up memory & cpu ++# cycles. ++export ASAN_OPTIONS=detect_leaks=$enable_lsan:quarantine_size_mb=0:malloc_context_size=0 + # Set argv[0] to the original binary name without the ".orig" suffix + exec -a "\$0" -- "${target}.orig" "\$@" + EOF diff --git a/0669-fstab-generator-support-swap-on-network-block-device.patch b/0669-fstab-generator-support-swap-on-network-block-device.patch new file mode 100644 index 0000000..73dff3b --- /dev/null +++ b/0669-fstab-generator-support-swap-on-network-block-device.patch @@ -0,0 +1,324 @@ +From 9864737eb2332696db1bb16b5049230af39badca Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 7 Apr 2026 11:16:42 +0200 +Subject: [PATCH] fstab-generator: support swap on network block devices + +Teach swap units to support the _netdev option as well, which should +make swaps on iSCSI possible. This mirrors the logic we already have for +regular mounts in both the fstab-generator and the core +(mount.c/swap.c). + +Co-developed-by: Claude Opus 4.6 +(cherry picked from commit 3d5bd67a2259e7a4edc27476d4cae049653c4414) + +Resolves: RHEL-128058 +--- + man/systemd.swap.xml | 30 ++++++++++-- + src/core/swap.c | 46 ++++++++++++++++--- + src/fstab-generator/fstab-generator.c | 16 +++++-- + src/shared/generator.c | 2 +- + .../systemd-remount-fs.service | 0 + .../sysroot.mount | 0 + .../50-netdev-dependencies.conf | 5 ++ + .../dev-sdx1.swap | 10 ++++ + .../systemd-remount-fs.service | 0 + .../remote-fs.target.requires/dev-sdx1.swap | 1 + + .../50-netdev-dependencies.conf | 5 ++ + .../dev-sdx1.swap | 10 ++++ + .../sysroot.mount | 0 + .../remote-fs.target.requires/dev-sdx1.swap | 1 + + .../test-21-swap-netdev.fstab.input | 1 + + 15 files changed, 113 insertions(+), 14 deletions(-) + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected.container.sysroot/local-fs.target.wants/systemd-remount-fs.service + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected.container/initrd-usr-fs.target.requires/sysroot.mount + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.device.d/50-netdev-dependencies.conf + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.swap + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/local-fs.target.wants/systemd-remount-fs.service + create mode 120000 test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/remote-fs.target.requires/dev-sdx1.swap + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.device.d/50-netdev-dependencies.conf + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.swap + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.expected/initrd-usr-fs.target.requires/sysroot.mount + create mode 120000 test/test-fstab-generator/test-21-swap-netdev.fstab.expected/remote-fs.target.requires/dev-sdx1.swap + create mode 100644 test/test-fstab-generator/test-21-swap-netdev.fstab.input + +diff --git a/man/systemd.swap.xml b/man/systemd.swap.xml +index f5e3c0742b..ccb6ca10cd 100644 +--- a/man/systemd.swap.xml ++++ b/man/systemd.swap.xml +@@ -90,9 +90,15 @@ + The following dependencies are added unless DefaultDependencies=no is set: + + +- Swap units automatically acquire a Conflicts= and a ++ Local swap units automatically acquire a Conflicts= and a + Before= dependency on umount.target so that they are deactivated at + shutdown as well as a Before=swap.target dependency. ++ ++ Network swap units (those with in their options) automatically acquire ++ After= dependencies on remote-fs-pre.target and ++ network.target, plus After= and Wants= dependencies ++ on network-online.target, and a Before= dependency on ++ remote-fs.target instead of swap.target. + + + +@@ -124,7 +130,8 @@ + + With , the swap unit + will not be added as a dependency for +- swap.target. This means that it will not ++ swap.target (or remote-fs.target for network swap devices, ++ see below). This means that it will not + be activated automatically during boot, unless it is pulled in + by some other unit. The option has the + opposite meaning and is the default. +@@ -138,8 +145,8 @@ + + With , the swap unit + will be only wanted, not required by +- swap.target. This means that the boot +- will continue even if this swap device is not activated ++ swap.target (or remote-fs.target for network swap ++ devices). This means that the boot will continue even if this swap device is not activated + successfully. + + +@@ -167,6 +174,21 @@ + + + ++ ++ ++ ++ ++ Marks this swap device as requiring network access. This is useful for swap on ++ network block devices (e.g. iSCSI). ++ ++ Network swap units are ordered between remote-fs-pre.target and ++ remote-fs.target, instead of being ordered before ++ swap.target. They also pull in network-online.target and ++ are ordered after it and network.target. ++ ++ ++ ++ + + + +diff --git a/src/core/swap.c b/src/core/swap.c +index eb596105ba..531dca1033 100644 +--- a/src/core/swap.c ++++ b/src/core/swap.c +@@ -220,6 +220,7 @@ static int swap_add_device_dependencies(Swap *s) { + } + + static int swap_add_default_dependencies(Swap *s) { ++ SwapParameters *p; + int r; + + assert(s); +@@ -233,13 +234,46 @@ static int swap_add_default_dependencies(Swap *s) { + if (detect_container() > 0) + return 0; + +- /* swap units generated for the swap dev links are missing the +- * ordering dep against the swap target. */ +- r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SWAP_TARGET, true, UNIT_DEPENDENCY_DEFAULT); +- if (r < 0) +- return r; ++ p = swap_get_parameters(s); ++ ++ if (p && fstab_test_option(p->options, "_netdev\0")) { ++ /* Network swap devices (those with _netdev in options) are routed through ++ * remote-fs.target instead of swap.target, mirroring how network mounts use ++ * remote-fs.target instead of local-fs.target. This avoids an ordering cycle: ++ * swap.target is pulled in at sysinit.target time, but network-online.target ++ * only comes after basic.target which is after sysinit.target. */ ++ r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOTE_FS_PRE_TARGET, ++ /* add_reference= */ true, UNIT_DEPENDENCY_DEFAULT); ++ if (r < 0) ++ return r; ++ ++ r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_REMOTE_FS_TARGET, ++ /* add_reference= */ true, UNIT_DEPENDENCY_DEFAULT); ++ if (r < 0) ++ return r; ++ ++ /* Pull in and order after network-online.target, analogous to ++ * mount_add_default_network_dependencies() for network mounts. */ ++ r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_NETWORK_TARGET, ++ /* add_reference= */ true, UNIT_DEPENDENCY_DEFAULT); ++ if (r < 0) ++ return r; ++ ++ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, ++ /* add_reference= */ true, UNIT_DEPENDENCY_DEFAULT); ++ if (r < 0) ++ return r; ++ } else { ++ /* swap units generated for the swap dev links are missing the ++ * ordering dep against the swap target. */ ++ r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SWAP_TARGET, ++ /* add_reference= */ true, UNIT_DEPENDENCY_DEFAULT); ++ if (r < 0) ++ return r; ++ } + +- return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, true, UNIT_DEPENDENCY_DEFAULT); ++ return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, ++ /* add_reference= */ true, UNIT_DEPENDENCY_DEFAULT); + } + + static int swap_verify(Swap *s) { +diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c +index 590042e992..5c26c52666 100644 +--- a/src/fstab-generator/fstab-generator.c ++++ b/src/fstab-generator/fstab-generator.c +@@ -217,6 +217,7 @@ static int add_swap( + + _cleanup_free_ char *name = NULL; + _cleanup_fclose_ FILE *f = NULL; ++ bool is_network; + int r; + + assert(what); +@@ -236,10 +237,12 @@ static int add_swap( + return true; + } + +- log_debug("Found swap entry what=%s makefs=%s growfs=%s pcrfs=%s noauto=%s nofail=%s", ++ is_network = fstab_test_option(options, "_netdev\0"); ++ ++ log_debug("Found swap entry what=%s makefs=%s growfs=%s pcrfs=%s noauto=%s nofail=%s netdev=%s", + what, + yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS), yes_no(flags & MOUNT_PCRFS), +- yes_no(flags & MOUNT_NOAUTO), yes_no(flags & MOUNT_NOFAIL)); ++ yes_no(flags & MOUNT_NOAUTO), yes_no(flags & MOUNT_NOFAIL), yes_no(is_network)); + + r = unit_name_from_path(what, ".swap", &name); + if (r < 0) +@@ -280,6 +283,12 @@ static int add_swap( + if (r < 0) + return r; + ++ if (is_network) { ++ r = generator_write_device_deps(arg_dest, what, /* where= */ NULL, options); ++ if (r < 0) ++ return r; ++ } ++ + if (flags & MOUNT_MAKEFS) { + r = generator_hook_up_mkswap(arg_dest, what); + if (r < 0) +@@ -293,7 +302,8 @@ static int add_swap( + log_warning("%s: measuring swap devices is currently unsupported.", what); + + if (!(flags & MOUNT_NOAUTO)) { +- r = generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, ++ const char *target = is_network ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_SWAP_TARGET; ++ r = generator_add_symlink(arg_dest, target, + (flags & MOUNT_NOFAIL) ? "wants" : "requires", name); + if (r < 0) + return r; +diff --git a/src/shared/generator.c b/src/shared/generator.c +index b3e57770aa..9011532d6b 100644 +--- a/src/shared/generator.c ++++ b/src/shared/generator.c +@@ -435,7 +435,7 @@ int generator_write_device_deps( + _cleanup_free_ char *node = NULL, *unit = NULL; + int r; + +- if (fstab_is_extrinsic(where, opts)) ++ if (where && fstab_is_extrinsic(where, opts)) + return 0; + + if (!fstab_test_option(opts, "_netdev\0")) +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.container.sysroot/local-fs.target.wants/systemd-remount-fs.service b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.container.sysroot/local-fs.target.wants/systemd-remount-fs.service +new file mode 100644 +index 0000000000..e69de29bb2 +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.container/initrd-usr-fs.target.requires/sysroot.mount b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.container/initrd-usr-fs.target.requires/sysroot.mount +new file mode 100644 +index 0000000000..e69de29bb2 +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.device.d/50-netdev-dependencies.conf b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.device.d/50-netdev-dependencies.conf +new file mode 100644 +index 0000000000..33d814c275 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.device.d/50-netdev-dependencies.conf +@@ -0,0 +1,5 @@ ++# Automatically generated by systemd-fstab-generator ++ ++[Unit] ++After=network-online.target network.target ++Wants=network-online.target +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.swap b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.swap +new file mode 100644 +index 0000000000..32f276c9e1 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/dev-sdx1.swap +@@ -0,0 +1,10 @@ ++# Automatically generated by systemd-fstab-generator ++ ++[Unit] ++Documentation=man:fstab(5) man:systemd-fstab-generator(8) ++SourcePath=/etc/fstab ++After=blockdev@dev-sdx1.target ++ ++[Swap] ++What=/dev/sdx1 ++Options=_netdev +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/local-fs.target.wants/systemd-remount-fs.service b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/local-fs.target.wants/systemd-remount-fs.service +new file mode 100644 +index 0000000000..e69de29bb2 +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/remote-fs.target.requires/dev-sdx1.swap b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/remote-fs.target.requires/dev-sdx1.swap +new file mode 120000 +index 0000000000..00f0c5ce66 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected.sysroot/remote-fs.target.requires/dev-sdx1.swap +@@ -0,0 +1 @@ ++../dev-sdx1.swap +\ No newline at end of file +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.device.d/50-netdev-dependencies.conf b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.device.d/50-netdev-dependencies.conf +new file mode 100644 +index 0000000000..33d814c275 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.device.d/50-netdev-dependencies.conf +@@ -0,0 +1,5 @@ ++# Automatically generated by systemd-fstab-generator ++ ++[Unit] ++After=network-online.target network.target ++Wants=network-online.target +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.swap b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.swap +new file mode 100644 +index 0000000000..32f276c9e1 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/dev-sdx1.swap +@@ -0,0 +1,10 @@ ++# Automatically generated by systemd-fstab-generator ++ ++[Unit] ++Documentation=man:fstab(5) man:systemd-fstab-generator(8) ++SourcePath=/etc/fstab ++After=blockdev@dev-sdx1.target ++ ++[Swap] ++What=/dev/sdx1 ++Options=_netdev +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/initrd-usr-fs.target.requires/sysroot.mount b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/initrd-usr-fs.target.requires/sysroot.mount +new file mode 100644 +index 0000000000..e69de29bb2 +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/remote-fs.target.requires/dev-sdx1.swap b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/remote-fs.target.requires/dev-sdx1.swap +new file mode 120000 +index 0000000000..00f0c5ce66 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.expected/remote-fs.target.requires/dev-sdx1.swap +@@ -0,0 +1 @@ ++../dev-sdx1.swap +\ No newline at end of file +diff --git a/test/test-fstab-generator/test-21-swap-netdev.fstab.input b/test/test-fstab-generator/test-21-swap-netdev.fstab.input +new file mode 100644 +index 0000000000..5f719a4202 +--- /dev/null ++++ b/test/test-fstab-generator/test-21-swap-netdev.fstab.input +@@ -0,0 +1 @@ ++/dev/sdx1 none swap _netdev 0 0 diff --git a/0670-man-add-tags-for-the-next-few-versions.patch b/0670-man-add-tags-for-the-next-few-versions.patch new file mode 100644 index 0000000..7562d8b --- /dev/null +++ b/0670-man-add-tags-for-the-next-few-versions.patch @@ -0,0 +1,27 @@ +From bd1621f973250525e4adef8f3466b56b98c4ff75 Mon Sep 17 00:00:00 2001 +From: Luca Boccassi +Date: Fri, 6 Mar 2026 00:25:10 +0000 +Subject: [PATCH] man: add tags for the next few versions + +(cherry picked from commit acce28884dd2e651d2bf09621c53b767edd4a050) + +Related: RHEL-128058 +--- + man/version-info.xml | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/man/version-info.xml b/man/version-info.xml +index c1138dfe22..ab0b122237 100644 +--- a/man/version-info.xml ++++ b/man/version-info.xml +@@ -84,6 +84,10 @@ + Added in version 258. + Added in version 259. + Added in version 260. ++ Added in version 261. ++ Added in version 262. ++ Added in version 263. ++ Added in version 264. + Added in rhel-8.0. + Added in rhel-8.1. + Added in rhel-8.2. diff --git a/0671-test-drop-some-extraneous-whitespaces.patch b/0671-test-drop-some-extraneous-whitespaces.patch new file mode 100644 index 0000000..97292bf --- /dev/null +++ b/0671-test-drop-some-extraneous-whitespaces.patch @@ -0,0 +1,29 @@ +From 553503cc7c263f2b22d8d3f2b9af608939e967ca Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 6 Mar 2026 16:09:21 +0100 +Subject: [PATCH] test: drop some extraneous whitespaces + +(cherry picked from commit 45830bc8d28513ebe7b57df178bfedeff75b80cf) + +Related: RHEL-155021 +--- + test/units/TEST-74-AUX-UTILS.userdbctl.sh | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/test/units/TEST-74-AUX-UTILS.userdbctl.sh b/test/units/TEST-74-AUX-UTILS.userdbctl.sh +index c6ecc4ea63..ef56114453 100755 +--- a/test/units/TEST-74-AUX-UTILS.userdbctl.sh ++++ b/test/units/TEST-74-AUX-UTILS.userdbctl.sh +@@ -19,9 +19,9 @@ u test-74-userdbctl - "Test user for TEST-74-AUX-UTILS.userdbctl.sh" / /bin/bash + EOF + + # Make sure that -F shows same data as if we'd ask directly +-userdbctl user root -j | userdbctl -F- user | cmp - <(userdbctl user root) +-userdbctl user test-74-userdbctl -j | userdbctl -F- user | cmp - <(userdbctl user test-74-userdbctl) +-userdbctl user 65534 -j | userdbctl -F- user | cmp - <(userdbctl user 65534) ++userdbctl user root -j | userdbctl -F- user | cmp - <(userdbctl user root) ++userdbctl user test-74-userdbctl -j | userdbctl -F- user | cmp - <(userdbctl user test-74-userdbctl) ++userdbctl user 65534 -j | userdbctl -F- user | cmp - <(userdbctl user 65534) + + userdbctl group root -j | userdbctl -F- group | cmp - <(userdbctl group root) + userdbctl group test-74-userdbctl -j | userdbctl -F- group | cmp - <(userdbctl group test-74-userdbctl) diff --git a/0672-shared-fix-segfault-when-processing-matchHostname-fi.patch b/0672-shared-fix-segfault-when-processing-matchHostname-fi.patch new file mode 100644 index 0000000..9e3914f --- /dev/null +++ b/0672-shared-fix-segfault-when-processing-matchHostname-fi.patch @@ -0,0 +1,52 @@ +From 3cf7e6cbb522532a87458036b6d3642500bfee3a Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 6 Mar 2026 16:09:35 +0100 +Subject: [PATCH] shared: fix segfault when processing matchHostname field + +Fix a typo which causes a segfault when processing a user record +with matchHostname when it's an array instead of a simple string: + +$ echo '{"userName":"crashhostarray","perMachine":[{"matchHostname":["host1","host2"],"locked":false}]}' | userdbctl -F - +Segmentation fault (core dumped) + +$ coredumpctl info +... + Message: Process 1172301 (userdbctl) of user 1000 dumped core. + + Module libz.so.1 from rpm zlib-ng-2.3.3-1.fc43.x86_64 + Module libcrypto.so.3 from rpm openssl-3.5.4-2.fc43.x86_64 + Stack trace of thread 1172301: + #0 0x00007fded7b3a656 __strcmp_evex (libc.so.6 + 0x159656) + #1 0x00007fded7e95397 per_machine_hostname_match (libsystemd-shared-260.so + 0x295397) + #2 0x00007fded7e955b5 per_machine_match (libsystemd-shared-260.so + 0x2955b5) + #3 0x00007fded7e957c6 dispatch_per_machine (libsystemd-shared-260.so + 0x2957c6) + #4 0x00007fded7e96c97 user_record_load (libsystemd-shared-260.so + 0x296c97) + #5 0x000000000040572d display_user (/home/fsumsal/repos/@systemd/systemd/build/userdbctl + 0x572d) + #6 0x00007fded7ea9727 dispatch_verb (libsystemd-shared-260.so + 0x2a9727) + #7 0x000000000041077c run (/home/fsumsal/repos/@systemd/systemd/build/userdbctl + 0x1077c) + #8 0x00000000004107ce main (/home/fsumsal/repos/@systemd/systemd/build/userdbctl + 0x107ce) + #9 0x00007fded79e45b5 __libc_start_call_main (libc.so.6 + 0x35b5) + #10 0x00007fded79e4668 __libc_start_main@@GLIBC_2.34 (libc.so.6 + 0x3668) + #11 0x00000000004038d5 _start (/home/fsumsal/repos/@systemd/systemd/build/userdbctl + 0x38d5) + ELF object binary architecture: AMD x86-64 + +(cherry picked from commit 1e2517bf2ee1b55c7c2406574f95b7d5788f6179) + +Resolves: RHEL-155021 +--- + src/shared/user-record.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/shared/user-record.c b/src/shared/user-record.c +index 6a1813c402..38d14cbca5 100644 +--- a/src/shared/user-record.c ++++ b/src/shared/user-record.c +@@ -1197,7 +1197,7 @@ int per_machine_hostname_match(sd_json_variant *hns, sd_json_dispatch_flags_t fl + continue; + } + +- if (streq(sd_json_variant_string(hns), hn)) ++ if (streq(sd_json_variant_string(e), hn)) + return true; + } + diff --git a/0673-shared-don-t-exclude-valid-min-max-values-for-cgroup.patch b/0673-shared-don-t-exclude-valid-min-max-values-for-cgroup.patch new file mode 100644 index 0000000..e077363 --- /dev/null +++ b/0673-shared-don-t-exclude-valid-min-max-values-for-cgroup.patch @@ -0,0 +1,57 @@ +From 08b4a2c4340aa8d4285f90b266aaae45cb3579aa Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 6 Mar 2026 16:36:52 +0100 +Subject: [PATCH] shared: don't exclude valid min/max values for cgroup weight + fields +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +1 and 10000 are valid cgroup weight values, but the condition was +incorrectly excluding them: + +$ echo '{"userName":"crashhostarray","cpuWeight":1}' | userdbctl -F - +:1:42: JSON field 'cpuWeight' is not in valid range 1…10000. + +$ echo '{"userName":"crashhostarray","cpuWeight":10000}' | userdbctl -F - +:1:42: JSON field 'cpuWeight' is not in valid range 1…10000. + +(cherry picked from commit 76ab7861ff8ce505cf8deff880ce2d6c1bd05e0c) + +Related: RHEL-155021 +--- + src/shared/user-record.c | 6 +++--- + test/units/TEST-74-AUX-UTILS.userdbctl.sh | 4 ++++ + 2 files changed, 7 insertions(+), 3 deletions(-) + +diff --git a/src/shared/user-record.c b/src/shared/user-record.c +index 38d14cbca5..ddfeaf6659 100644 +--- a/src/shared/user-record.c ++++ b/src/shared/user-record.c +@@ -590,11 +590,11 @@ static int json_dispatch_weight(const char *name, sd_json_variant *variant, sd_j + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name)); + + k = sd_json_variant_unsigned(variant); +- if (k <= CGROUP_WEIGHT_MIN || k >= CGROUP_WEIGHT_MAX) ++ if (k < CGROUP_WEIGHT_MIN || k > CGROUP_WEIGHT_MAX) + return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), + "JSON field '%s' is not in valid range %" PRIu64 "%s%" PRIu64 ".", +- strna(name), (uint64_t) CGROUP_WEIGHT_MIN, +- special_glyph(SPECIAL_GLYPH_ELLIPSIS), (uint64_t) CGROUP_WEIGHT_MAX); ++ strna(name), CGROUP_WEIGHT_MIN, ++ special_glyph(SPECIAL_GLYPH_ELLIPSIS), CGROUP_WEIGHT_MAX); + + *weight = k; + return 0; +diff --git a/test/units/TEST-74-AUX-UTILS.userdbctl.sh b/test/units/TEST-74-AUX-UTILS.userdbctl.sh +index ef56114453..55963ccd43 100755 +--- a/test/units/TEST-74-AUX-UTILS.userdbctl.sh ++++ b/test/units/TEST-74-AUX-UTILS.userdbctl.sh +@@ -26,3 +26,7 @@ userdbctl user 65534 -j | userdbctl -F- user | cmp - <(userdbctl user 65534) + userdbctl group root -j | userdbctl -F- group | cmp - <(userdbctl group root) + userdbctl group test-74-userdbctl -j | userdbctl -F- group | cmp - <(userdbctl group test-74-userdbctl) + userdbctl group 65534 -j | userdbctl -F- group | cmp - <(userdbctl group 65534) ++ ++# Probe specific user records ++echo '{"userName":"weightmin","cpuWeight":1,"ioWeight":1}' | userdbctl -F - ++echo '{"userName":"weightmax","cpuWeight":10000,"ioWeight":10000}' | userdbctl -F - diff --git a/0674-shared-don-t-leak-memory-from-array-fields.patch b/0674-shared-don-t-leak-memory-from-array-fields.patch new file mode 100644 index 0000000..8f1c39a --- /dev/null +++ b/0674-shared-don-t-leak-memory-from-array-fields.patch @@ -0,0 +1,75 @@ +From ef7a20ffd480b8c6021cf1277ac3f8c4293d48c1 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 6 Mar 2026 17:16:31 +0100 +Subject: [PATCH] shared: don't leak memory from array fields + +The fido2_hmac_salt/fido2_hmac_credential/recovery_key fields kept +leaking memory as the array itself wasn't deallocated after deallocating +each of its elements data: + +$ build-san/userdbctl -F fuzz-corpus-userdb/auth-fido2.json +... +================================================================= +==1292840==ERROR: LeakSanitizer: detected memory leaks + +Direct leak of 112 byte(s) in 1 object(s) allocated from: + #0 0x7f56f00e5e4b in realloc.part.0 (/lib64/libasan.so.8+0xe5e4b) (BuildId: 25975f766867e9e604dc5a71a8befeaed3301942) + #1 0x7f56ed869e42 in greedy_realloc ../src/basic/alloc-util.c:65 + #2 0x7f56ed7ff5e9 in dispatch_fido2_hmac_salt ../src/shared/user-record.c:836 + #3 0x7f56edd73cbc in sd_json_dispatch_full ../src/libsystemd/sd-json/sd-json.c:5204 + #4 0x7f56edd745fc in sd_json_dispatch ../src/libsystemd/sd-json/sd-json.c:5276 + #5 0x7f56ed80100b in dispatch_privileged ../src/shared/user-record.c:998 + #6 0x7f56edd73cbc in sd_json_dispatch_full ../src/libsystemd/sd-json/sd-json.c:5204 + #7 0x7f56edd745fc in sd_json_dispatch ../src/libsystemd/sd-json/sd-json.c:5276 + #8 0x7f56ed80622c in user_record_load ../src/shared/user-record.c:1697 + #9 0x000000408c15 in display_user ../src/userdb/userdbctl.c:447 + #10 0x7f56ed83cc9a in dispatch_verb ../src/shared/verbs.c:137 + #11 0x00000041df2b in run ../src/userdb/userdbctl.c:1908 + #12 0x00000041dfbe in main ../src/userdb/userdbctl.c:1911 + #13 0x7f56ec8105b4 in __libc_start_call_main (/lib64/libc.so.6+0x35b4) (BuildId: 2b5beec0fd24fe9c9f43eddfdd5facf0b8a1b805) + #14 0x7f56ec810667 in __libc_start_main@@GLIBC_2.34 (/lib64/libc.so.6+0x3667) (BuildId: 2b5beec0fd24fe9c9f43eddfdd5facf0b8a1b805) + #15 0x000000404a44 in _start (/home/fsumsal/repos/@systemd/systemd/build-san/userdbctl+0x404a44) (BuildId: 19e8b7e7b7038d2cea20bc18a55bea2a9e4406d5) + +Direct leak of 64 byte(s) in 1 object(s) allocated from: + #0 0x7f56f00e5e4b in realloc.part.0 (/lib64/libasan.so.8+0xe5e4b) (BuildId: 25975f766867e9e604dc5a71a8befeaed3301942) + #1 0x7f56ed869e42 in greedy_realloc ../src/basic/alloc-util.c:65 + #2 0x7f56ed7fe779 in dispatch_fido2_hmac_credential_array ../src/shared/user-record.c:775 + #3 0x7f56edd73cbc in sd_json_dispatch_full ../src/libsystemd/sd-json/sd-json.c:5204 + #4 0x7f56edd745fc in sd_json_dispatch ../src/libsystemd/sd-json/sd-json.c:5276 + #5 0x7f56ed80622c in user_record_load ../src/shared/user-record.c:1697 + #6 0x000000408c15 in display_user ../src/userdb/userdbctl.c:447 + #7 0x7f56ed83cc9a in dispatch_verb ../src/shared/verbs.c:137 + #8 0x00000041df2b in run ../src/userdb/userdbctl.c:1908 + #9 0x00000041dfbe in main ../src/userdb/userdbctl.c:1911 + #10 0x7f56ec8105b4 in __libc_start_call_main (/lib64/libc.so.6+0x35b4) (BuildId: 2b5beec0fd24fe9c9f43eddfdd5facf0b8a1b805) + #11 0x7f56ec810667 in __libc_start_main@@GLIBC_2.34 (/lib64/libc.so.6+0x3667) (BuildId: 2b5beec0fd24fe9c9f43eddfdd5facf0b8a1b805) + #12 0x000000404a44 in _start (/home/fsumsal/repos/@systemd/systemd/build-san/userdbctl+0x404a44) (BuildId: 19e8b7e7b7038d2cea20bc18a55bea2a9e4406d5) + +SUMMARY: AddressSanitizer: 176 byte(s) leaked in 2 allocation(s). +(cherry picked from commit 3c7bd947b29775c6dd035a27462f445d5945447b) + +Related: RHEL-155021 +--- + src/shared/user-record.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/shared/user-record.c b/src/shared/user-record.c +index ddfeaf6659..f4febcdebe 100644 +--- a/src/shared/user-record.c ++++ b/src/shared/user-record.c +@@ -205,12 +205,15 @@ static UserRecord* user_record_free(UserRecord *h) { + + for (size_t i = 0; i < h->n_fido2_hmac_credential; i++) + fido2_hmac_credential_done(h->fido2_hmac_credential + i); ++ free(h->fido2_hmac_credential); + for (size_t i = 0; i < h->n_fido2_hmac_salt; i++) + fido2_hmac_salt_done(h->fido2_hmac_salt + i); ++ free(h->fido2_hmac_salt); + + strv_free(h->recovery_key_type); + for (size_t i = 0; i < h->n_recovery_key; i++) + recovery_key_done(h->recovery_key + i); ++ free(h->recovery_key); + + strv_free(h->self_modifiable_fields); + strv_free(h->self_modifiable_blobs); diff --git a/0675-man-fix-short-option-for-userdbctl-s-from-file.patch b/0675-man-fix-short-option-for-userdbctl-s-from-file.patch new file mode 100644 index 0000000..42efbfb --- /dev/null +++ b/0675-man-fix-short-option-for-userdbctl-s-from-file.patch @@ -0,0 +1,25 @@ +From 66fb573d491e479eff79866d0709d0af29f6ba46 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 6 Mar 2026 17:30:52 +0100 +Subject: [PATCH] man: fix short option for userdbctl's --from-file= + +(cherry picked from commit ad1c7df5f93a2602e73ba5673e483b3e5d1b5422) + +Related: RHEL-155021 +--- + man/userdbctl.xml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/man/userdbctl.xml b/man/userdbctl.xml +index 4da9762ab0..aefeb740f2 100644 +--- a/man/userdbctl.xml ++++ b/man/userdbctl.xml +@@ -254,7 +254,7 @@ + + + +- ++ + + When used with the user or group command, read + the user definition in JSON format from the specified file, instead of querying it from the diff --git a/0676-fuzz-add-a-fuzzer-for-user-records.patch b/0676-fuzz-add-a-fuzzer-for-user-records.patch new file mode 100644 index 0000000..e73ca77 --- /dev/null +++ b/0676-fuzz-add-a-fuzzer-for-user-records.patch @@ -0,0 +1,1376 @@ +From 02bffe9d102a8a276bef8abf1cc473b6a86a1760 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 6 Mar 2026 17:58:02 +0100 +Subject: [PATCH] fuzz: add a fuzzer for user records + +Add a simple fuzzer that verifies our machinery for parsing user records +from JSON works as intended. + +The initial corpus was created with the help of Claude, so we have a +bunch of valid user records with as much fields as possible for the +initial corpus. + +(cherry picked from commit be0db50cadadb35fdbc117ed68e133f34604b97b) + +Related: RHEL-155021 +--- + src/fuzz/fuzz-user-record.c | 46 ++++ + src/fuzz/meson.build | 1 + + test/fuzz/fuzz-user-record/auth-fido2.json | 40 +++ + test/fuzz/fuzz-user-record/auth-pkcs11.json | 33 +++ + test/fuzz/fuzz-user-record/auth-recovery.json | 27 ++ + .../fuzz-user-record/auto-resize-grow.json | 14 + + .../fuzz-user-record/auto-resize-modes.json | 14 + + .../fuzz-user-record/auto-resize-off.json | 14 + + test/fuzz/fuzz-user-record/basic-regular.json | 9 + + test/fuzz/fuzz-user-record/basic-system.json | 7 + + .../fuzz-user-record/capabilities-full.json | 56 ++++ + test/fuzz/fuzz-user-record/default-area.json | 16 ++ + .../disposition-container.json | 8 + + .../fuzz-user-record/disposition-dynamic.json | 9 + + .../fuzz-user-record/disposition-foreign.json | 8 + + .../disposition-intrinsic.json | 8 + + .../disposition-reserved.json | 9 + + .../fuzz-user-record/edge-empty-arrays.json | 34 +++ + .../fuzz-user-record/edge-long-strings.json | 24 ++ + .../fuzz-user-record/edge-max-values.json | 41 +++ + .../fuzz-user-record/edge-min-values.json | 41 +++ + .../edge-missing-optionals.json | 9 + + .../fuzz-user-record/edge-null-values.json | 18 ++ + .../edge-unicode-strings.json | 21 ++ + test/fuzz/fuzz-user-record/full-featured.json | 245 ++++++++++++++++++ + test/fuzz/fuzz-user-record/luks-complete.json | 36 +++ + test/fuzz/fuzz-user-record/minimal.json | 3 + + .../fuzz-user-record/password-policy.json | 24 ++ + .../fuzz-user-record/per-machine-complex.json | 58 +++++ + test/fuzz/fuzz-user-record/rlimits-all.json | 27 ++ + test/fuzz/fuzz-user-record/session-prefs.json | 14 + + test/fuzz/fuzz-user-record/ssh-keys.json | 18 ++ + test/fuzz/fuzz-user-record/storage-cifs.json | 15 ++ + .../fuzz-user-record/storage-classic.json | 10 + + .../fuzz-user-record/storage-directory.json | 13 + + .../fuzz-user-record/storage-fscrypt.json | 14 + + .../fuzz-user-record/storage-subvolume.json | 14 + + .../fuzz-user-record/time-constraints.json | 13 + + test/fuzz/fuzz-user-record/tmpfs-limits.json | 13 + + test/fuzz/fuzz-user-record/with-realm.json | 10 + + 40 files changed, 1034 insertions(+) + create mode 100644 src/fuzz/fuzz-user-record.c + create mode 100644 test/fuzz/fuzz-user-record/auth-fido2.json + create mode 100644 test/fuzz/fuzz-user-record/auth-pkcs11.json + create mode 100644 test/fuzz/fuzz-user-record/auth-recovery.json + create mode 100644 test/fuzz/fuzz-user-record/auto-resize-grow.json + create mode 100644 test/fuzz/fuzz-user-record/auto-resize-modes.json + create mode 100644 test/fuzz/fuzz-user-record/auto-resize-off.json + create mode 100644 test/fuzz/fuzz-user-record/basic-regular.json + create mode 100644 test/fuzz/fuzz-user-record/basic-system.json + create mode 100644 test/fuzz/fuzz-user-record/capabilities-full.json + create mode 100644 test/fuzz/fuzz-user-record/default-area.json + create mode 100644 test/fuzz/fuzz-user-record/disposition-container.json + create mode 100644 test/fuzz/fuzz-user-record/disposition-dynamic.json + create mode 100644 test/fuzz/fuzz-user-record/disposition-foreign.json + create mode 100644 test/fuzz/fuzz-user-record/disposition-intrinsic.json + create mode 100644 test/fuzz/fuzz-user-record/disposition-reserved.json + create mode 100644 test/fuzz/fuzz-user-record/edge-empty-arrays.json + create mode 100644 test/fuzz/fuzz-user-record/edge-long-strings.json + create mode 100644 test/fuzz/fuzz-user-record/edge-max-values.json + create mode 100644 test/fuzz/fuzz-user-record/edge-min-values.json + create mode 100644 test/fuzz/fuzz-user-record/edge-missing-optionals.json + create mode 100644 test/fuzz/fuzz-user-record/edge-null-values.json + create mode 100644 test/fuzz/fuzz-user-record/edge-unicode-strings.json + create mode 100644 test/fuzz/fuzz-user-record/full-featured.json + create mode 100644 test/fuzz/fuzz-user-record/luks-complete.json + create mode 100644 test/fuzz/fuzz-user-record/minimal.json + create mode 100644 test/fuzz/fuzz-user-record/password-policy.json + create mode 100644 test/fuzz/fuzz-user-record/per-machine-complex.json + create mode 100644 test/fuzz/fuzz-user-record/rlimits-all.json + create mode 100644 test/fuzz/fuzz-user-record/session-prefs.json + create mode 100644 test/fuzz/fuzz-user-record/ssh-keys.json + create mode 100644 test/fuzz/fuzz-user-record/storage-cifs.json + create mode 100644 test/fuzz/fuzz-user-record/storage-classic.json + create mode 100644 test/fuzz/fuzz-user-record/storage-directory.json + create mode 100644 test/fuzz/fuzz-user-record/storage-fscrypt.json + create mode 100644 test/fuzz/fuzz-user-record/storage-subvolume.json + create mode 100644 test/fuzz/fuzz-user-record/time-constraints.json + create mode 100644 test/fuzz/fuzz-user-record/tmpfs-limits.json + create mode 100644 test/fuzz/fuzz-user-record/with-realm.json + +diff --git a/src/fuzz/fuzz-user-record.c b/src/fuzz/fuzz-user-record.c +new file mode 100644 +index 0000000000..c520e6a03b +--- /dev/null ++++ b/src/fuzz/fuzz-user-record.c +@@ -0,0 +1,46 @@ ++/* SPDX-License-Identifier: LGPL-2.1-or-later */ ++ ++#include ++ ++#include "alloc-util.h" ++#include "fuzz.h" ++#include "user-record.h" ++ ++#include "sd-json.h" ++ ++int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ++ _cleanup_(user_record_unrefp) UserRecord *ur = NULL; ++ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; ++ _cleanup_free_ char *str = NULL; ++ unsigned line = 0; ++ int r; ++ ++ if (outside_size_range(size, 0, 65536)) ++ return 0; ++ ++ assert_se(str = memdup_suffix0(data, size)); ++ assert_se(ur = user_record_new()); ++ ++ fuzz_setup_logging(); ++ ++ r = sd_json_parse(str, 0, &v, &line, /* reterr_column= */ NULL); ++ if (r < 0) { ++ (void) log_syntax(/* unit= */ NULL, LOG_DEBUG, "", line, r, "JSON parse failure."); ++ return 0; ++ } ++ ++ r = user_record_load(ur, v, USER_RECORD_LOAD_FULL|USER_RECORD_PERMISSIVE); ++ if (r >= 0) { ++ /* We have a valid record, so let's excercise a couple more functions */ ++ _cleanup_(user_record_unrefp) UserRecord *cloned = NULL; ++ (void) user_record_clone(ur, USER_RECORD_LOAD_FULL, &cloned); ++ ++ (void) user_record_test_blocked(ur); ++ (void) user_record_test_password_change_required(ur); ++ (void) user_record_can_authenticate(ur); ++ (void) user_record_luks_discard(ur); ++ (void) user_record_drop_caches(ur); ++ } ++ ++ return 0; ++} +diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build +index 8c1b2e91ea..e25c62ea22 100644 +--- a/src/fuzz/meson.build ++++ b/src/fuzz/meson.build +@@ -11,6 +11,7 @@ simple_fuzzers += files( + 'fuzz-json.c', + 'fuzz-time-util.c', + 'fuzz-udev-database.c', ++ 'fuzz-user-record.c', + 'fuzz-varlink.c', + 'fuzz-varlink-idl.c', + ) +diff --git a/test/fuzz/fuzz-user-record/auth-fido2.json b/test/fuzz/fuzz-user-record/auth-fido2.json +new file mode 100644 +index 0000000000..cd8282f5bf +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/auth-fido2.json +@@ -0,0 +1,40 @@ ++{ ++ "userName": "fido2user", ++ "realName": "FIDO2 Security Key User", ++ "uid": 2002, ++ "gid": 2002, ++ "homeDirectory": "/home/fido2user", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "luks", ++ "imagePath": "/home/fido2user.home", ++ "fido2HmacCredential": [ ++ "AQIDBAUAAQ==", ++ "BQYHCAkKCw==" ++ ], ++ "privileged": { ++ "fido2HmacSalt": [ ++ { ++ "credential": "AQIDBAUAAQ==", ++ "salt": "Zmlyc3RzYWx0Zm9yZmlkbzJ0ZXN0aW5n", ++ "hashedPassword": "$6$fido2salt1$hashedpasswordforfido2credential1", ++ "up": true, ++ "uv": false, ++ "clientPin": false ++ }, ++ { ++ "credential": "BQYHCAkKCw==", ++ "salt": "c2Vjb25kc2FsdGZvcmZpZG8ydGVzdA==", ++ "hashedPassword": "$6$fido2salt2$hashedpasswordforfido2credential2", ++ "up": true, ++ "uv": true, ++ "clientPin": true ++ } ++ ] ++ }, ++ "secret": { ++ "tokenPin": ["1234"], ++ "fido2UserPresencePermitted": true, ++ "fido2UserVerificationPermitted": true ++ } ++} +diff --git a/test/fuzz/fuzz-user-record/auth-pkcs11.json b/test/fuzz/fuzz-user-record/auth-pkcs11.json +new file mode 100644 +index 0000000000..d4604e8f0b +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/auth-pkcs11.json +@@ -0,0 +1,33 @@ ++{ ++ "userName": "pkcs11user", ++ "realName": "PKCS#11 Token User", ++ "uid": 2001, ++ "gid": 2001, ++ "homeDirectory": "/home/pkcs11user", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "luks", ++ "imagePath": "/home/pkcs11user.home", ++ "pkcs11TokenUri": [ ++ "pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=00000000;token=pkcs11user", ++ "pkcs11:model=YubiKey;manufacturer=Yubico;serial=12345678;token=PIV" ++ ], ++ "privileged": { ++ "pkcs11EncryptedKey": [ ++ { ++ "uri": "pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=00000000;token=pkcs11user", ++ "data": "SGVsbG8gV29ybGQhIFRoaXMgaXMgYSB0ZXN0IGVuY3J5cHRlZCBrZXkgZm9yIFBLQ1MjMTEu", ++ "hashedPassword": "$6$pkcs11salt$encryptedkeyhashforpkcs11authentication" ++ }, ++ { ++ "uri": "pkcs11:model=YubiKey;manufacturer=Yubico;serial=12345678;token=PIV", ++ "data": "QW5vdGhlciBlbmNyeXB0ZWQga2V5IGZvciBhIGRpZmZlcmVudCB0b2tlbi4=", ++ "hashedPassword": "$6$yubisalt$encryptedkeyhashforyubikey" ++ } ++ ] ++ }, ++ "secret": { ++ "tokenPin": ["123456"], ++ "pkcs11ProtectedAuthenticationPathPermitted": true ++ } ++} +diff --git a/test/fuzz/fuzz-user-record/auth-recovery.json b/test/fuzz/fuzz-user-record/auth-recovery.json +new file mode 100644 +index 0000000000..f704e1ec1c +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/auth-recovery.json +@@ -0,0 +1,27 @@ ++{ ++ "userName": "recoveryuser", ++ "realName": "Recovery Key User", ++ "uid": 2003, ++ "gid": 2003, ++ "homeDirectory": "/home/recoveryuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "luks", ++ "imagePath": "/home/recoveryuser.home", ++ "recoveryKeyType": ["modhex64", "modhex64"], ++ "privileged": { ++ "hashedPassword": [ ++ "$6$mainsalt$mainpasswordhashforrecoveryuser" ++ ], ++ "recoveryKey": [ ++ { ++ "type": "modhex64", ++ "hashedPassword": "$6$recovery1$hashedrecoverykey1" ++ }, ++ { ++ "type": "modhex64", ++ "hashedPassword": "$6$recovery2$hashedrecoverykey2" ++ } ++ ] ++ } ++} +diff --git a/test/fuzz/fuzz-user-record/auto-resize-grow.json b/test/fuzz/fuzz-user-record/auto-resize-grow.json +new file mode 100644 +index 0000000000..d72a797a11 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/auto-resize-grow.json +@@ -0,0 +1,14 @@ ++{ ++ "userName": "growonly", ++ "realName": "Grow Only User", ++ "uid": 3008, ++ "gid": 3008, ++ "homeDirectory": "/home/growonly", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "luks", ++ "imagePath": "/home/growonly.home", ++ "diskSize": 53687091200, ++ "autoResizeMode": "grow", ++ "rebalanceWeight": true ++} +diff --git a/test/fuzz/fuzz-user-record/auto-resize-modes.json b/test/fuzz/fuzz-user-record/auto-resize-modes.json +new file mode 100644 +index 0000000000..75d50aed7b +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/auto-resize-modes.json +@@ -0,0 +1,14 @@ ++{ ++ "userName": "autoresizeuser", ++ "realName": "Auto Resize Test User", ++ "uid": 3006, ++ "gid": 3006, ++ "homeDirectory": "/home/autoresizeuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "luks", ++ "imagePath": "/home/autoresizeuser.home", ++ "diskSize": 53687091200, ++ "autoResizeMode": "shrink-and-grow", ++ "rebalanceWeight": 100 ++} +diff --git a/test/fuzz/fuzz-user-record/auto-resize-off.json b/test/fuzz/fuzz-user-record/auto-resize-off.json +new file mode 100644 +index 0000000000..8479985aca +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/auto-resize-off.json +@@ -0,0 +1,14 @@ ++{ ++ "userName": "noautoresize", ++ "realName": "No Auto Resize User", ++ "uid": 3007, ++ "gid": 3007, ++ "homeDirectory": "/home/noautoresize", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "luks", ++ "imagePath": "/home/noautoresize.home", ++ "diskSize": 53687091200, ++ "autoResizeMode": "off", ++ "rebalanceWeight": false ++} +diff --git a/test/fuzz/fuzz-user-record/basic-regular.json b/test/fuzz/fuzz-user-record/basic-regular.json +new file mode 100644 +index 0000000000..594f348aae +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/basic-regular.json +@@ -0,0 +1,9 @@ ++{ ++ "userName": "testuser", ++ "realName": "Test User", ++ "uid": 1000, ++ "gid": 1000, ++ "homeDirectory": "/home/testuser", ++ "shell": "/bin/bash", ++ "disposition": "regular" ++} +diff --git a/test/fuzz/fuzz-user-record/basic-system.json b/test/fuzz/fuzz-user-record/basic-system.json +new file mode 100644 +index 0000000000..9cfb3a2f63 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/basic-system.json +@@ -0,0 +1,7 @@ ++{ ++ "userName": "httpd", ++ "uid": 473, ++ "gid": 473, ++ "disposition": "system", ++ "locked": true ++} +diff --git a/test/fuzz/fuzz-user-record/capabilities-full.json b/test/fuzz/fuzz-user-record/capabilities-full.json +new file mode 100644 +index 0000000000..08d5f362be +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/capabilities-full.json +@@ -0,0 +1,56 @@ ++{ ++ "userName": "capuser", ++ "realName": "Capabilities Test User", ++ "uid": 3010, ++ "gid": 3010, ++ "homeDirectory": "/home/capuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "capabilityBoundingSet": [ ++ "CAP_AUDIT_CONTROL", ++ "CAP_AUDIT_READ", ++ "CAP_AUDIT_WRITE", ++ "CAP_BLOCK_SUSPEND", ++ "CAP_BPF", ++ "CAP_CHECKPOINT_RESTORE", ++ "CAP_CHOWN", ++ "CAP_DAC_OVERRIDE", ++ "CAP_DAC_READ_SEARCH", ++ "CAP_FOWNER", ++ "CAP_FSETID", ++ "CAP_IPC_LOCK", ++ "CAP_IPC_OWNER", ++ "CAP_KILL", ++ "CAP_LEASE", ++ "CAP_LINUX_IMMUTABLE", ++ "CAP_MAC_ADMIN", ++ "CAP_MAC_OVERRIDE", ++ "CAP_MKNOD", ++ "CAP_NET_ADMIN", ++ "CAP_NET_BIND_SERVICE", ++ "CAP_NET_BROADCAST", ++ "CAP_NET_RAW", ++ "CAP_PERFMON", ++ "CAP_SETFCAP", ++ "CAP_SETGID", ++ "CAP_SETPCAP", ++ "CAP_SETUID", ++ "CAP_SYS_ADMIN", ++ "CAP_SYS_BOOT", ++ "CAP_SYS_CHROOT", ++ "CAP_SYS_MODULE", ++ "CAP_SYS_NICE", ++ "CAP_SYS_PACCT", ++ "CAP_SYS_PTRACE", ++ "CAP_SYS_RAWIO", ++ "CAP_SYS_RESOURCE", ++ "CAP_SYS_TIME", ++ "CAP_SYS_TTY_CONFIG", ++ "CAP_SYSLOG", ++ "CAP_WAKE_ALARM" ++ ], ++ "capabilityAmbientSet": [ ++ "CAP_NET_BIND_SERVICE", ++ "CAP_NET_RAW" ++ ] ++} +diff --git a/test/fuzz/fuzz-user-record/default-area.json b/test/fuzz/fuzz-user-record/default-area.json +new file mode 100644 +index 0000000000..b27f40b5d9 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/default-area.json +@@ -0,0 +1,16 @@ ++{ ++ "userName": "areauser", ++ "realName": "Default Area Test User", ++ "uid": 3012, ++ "gid": 3012, ++ "homeDirectory": "/home/areauser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "defaultArea": "work", ++ "perMachine": [ ++ { ++ "matchHostname": "personal", ++ "defaultArea": "personal" ++ } ++ ] ++} +diff --git a/test/fuzz/fuzz-user-record/disposition-container.json b/test/fuzz/fuzz-user-record/disposition-container.json +new file mode 100644 +index 0000000000..67d5995467 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/disposition-container.json +@@ -0,0 +1,8 @@ ++{ ++ "userName": "containeruser", ++ "uid": 100000, ++ "gid": 100000, ++ "homeDirectory": "/home/containeruser", ++ "shell": "/bin/sh", ++ "disposition": "container" ++} +diff --git a/test/fuzz/fuzz-user-record/disposition-dynamic.json b/test/fuzz/fuzz-user-record/disposition-dynamic.json +new file mode 100644 +index 0000000000..1678d99486 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/disposition-dynamic.json +@@ -0,0 +1,9 @@ ++{ ++ "userName": "dynamicuser", ++ "uid": 61234, ++ "gid": 61234, ++ "homeDirectory": "/run/dynamicuser", ++ "shell": "/usr/sbin/nologin", ++ "disposition": "dynamic", ++ "locked": true ++} +diff --git a/test/fuzz/fuzz-user-record/disposition-foreign.json b/test/fuzz/fuzz-user-record/disposition-foreign.json +new file mode 100644 +index 0000000000..9f3b2ff18f +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/disposition-foreign.json +@@ -0,0 +1,8 @@ ++{ ++ "userName": "foreignuser", ++ "uid": 200000, ++ "gid": 200000, ++ "homeDirectory": "/home/foreign", ++ "shell": "/bin/sh", ++ "disposition": "foreign" ++} +diff --git a/test/fuzz/fuzz-user-record/disposition-intrinsic.json b/test/fuzz/fuzz-user-record/disposition-intrinsic.json +new file mode 100644 +index 0000000000..9a66fa1b4f +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/disposition-intrinsic.json +@@ -0,0 +1,8 @@ ++{ ++ "userName": "root", ++ "uid": 0, ++ "gid": 0, ++ "homeDirectory": "/root", ++ "shell": "/bin/bash", ++ "disposition": "intrinsic" ++} +diff --git a/test/fuzz/fuzz-user-record/disposition-reserved.json b/test/fuzz/fuzz-user-record/disposition-reserved.json +new file mode 100644 +index 0000000000..74cf408546 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/disposition-reserved.json +@@ -0,0 +1,9 @@ ++{ ++ "userName": "reserveduser", ++ "uid": 999, ++ "gid": 999, ++ "homeDirectory": "/nonexistent", ++ "shell": "/usr/sbin/nologin", ++ "disposition": "reserved", ++ "locked": true ++} +diff --git a/test/fuzz/fuzz-user-record/edge-empty-arrays.json b/test/fuzz/fuzz-user-record/edge-empty-arrays.json +new file mode 100644 +index 0000000000..8e46c3d31e +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/edge-empty-arrays.json +@@ -0,0 +1,34 @@ ++{ ++ "userName": "emptyarrays", ++ "realName": "Empty Arrays Test User", ++ "uid": 3001, ++ "gid": 3001, ++ "homeDirectory": "/home/emptyarrays", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "aliases": [], ++ "environment": [], ++ "additionalLanguages": [], ++ "memberOf": [], ++ "capabilityBoundingSet": [], ++ "capabilityAmbientSet": [], ++ "pkcs11TokenUri": [], ++ "fido2HmacCredential": [], ++ "recoveryKeyType": [], ++ "selfModifiableFields": [], ++ "selfModifiableBlobs": [], ++ "selfModifiablePrivileged": [], ++ "privileged": { ++ "hashedPassword": [], ++ "sshAuthorizedKeys": [], ++ "pkcs11EncryptedKey": [], ++ "fido2HmacSalt": [], ++ "recoveryKey": [] ++ }, ++ "perMachine": [], ++ "signature": [], ++ "secret": { ++ "password": [], ++ "tokenPin": [] ++ } ++} +diff --git a/test/fuzz/fuzz-user-record/edge-long-strings.json b/test/fuzz/fuzz-user-record/edge-long-strings.json +new file mode 100644 +index 0000000000..b7883637e7 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/edge-long-strings.json +@@ -0,0 +1,24 @@ ++{ ++ "userName": "longstringsuser", ++ "realName": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", ++ "uid": 3004, ++ "gid": 3004, ++ "homeDirectory": "/home/longstringsuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "location": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", ++ "environment": [ ++ "LONGVAR=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" ++ ], ++ "memberOf": [ ++ "group1", "group2", "group3", "group4", "group5", ++ "group6", "group7", "group8", "group9", "group10", ++ "group11", "group12", "group13", "group14", "group15", ++ "group16", "group17", "group18", "group19", "group20" ++ ], ++ "privileged": { ++ "sshAuthorizedKeys": [ ++ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDVeryLongKeyDataHereThatGoesOnAndOnAndOnForAWhileToTestLongSSHKeysAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA user@host" ++ ] ++ } ++} +diff --git a/test/fuzz/fuzz-user-record/edge-max-values.json b/test/fuzz/fuzz-user-record/edge-max-values.json +new file mode 100644 +index 0000000000..52a34109f7 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/edge-max-values.json +@@ -0,0 +1,41 @@ ++{ ++ "userName": "maxuser", ++ "realName": "Maximum Values Test User", ++ "uid": 4294967294, ++ "gid": 4294967294, ++ "homeDirectory": "/home/maxuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "umask": 511, ++ "niceLevel": 19, ++ "cpuWeight": 10000, ++ "ioWeight": 10000, ++ "diskSize": 18446744073709551615, ++ "diskSizeRelative": 4294967295, ++ "tasksMax": 18446744073709551614, ++ "memoryHigh": 18446744073709551614, ++ "memoryMax": 18446744073709551614, ++ "accessMode": 511, ++ "luksPbkdfForceIterations": 18446744073709551615, ++ "luksPbkdfTimeCostUSec": 18446744073709551615, ++ "luksPbkdfMemoryCost": 18446744073709551615, ++ "luksPbkdfParallelThreads": 18446744073709551615, ++ "luksSectorSize": 4096, ++ "luksVolumeKeySize": 512, ++ "rebalanceWeight": 10000, ++ "rateLimitIntervalUSec": 18446744073709551615, ++ "rateLimitBurst": 18446744073709551615, ++ "stopDelayUSec": 18446744073709551615, ++ "lastChangeUSec": 18446744073709551615, ++ "lastPasswordChangeUSec": 18446744073709551615, ++ "notBeforeUSec": 0, ++ "notAfterUSec": 18446744073709551615, ++ "passwordChangeMinUSec": 0, ++ "passwordChangeMaxUSec": 18446744073709551615, ++ "passwordChangeWarnUSec": 18446744073709551615, ++ "passwordChangeInactiveUSec": 18446744073709551615, ++ "tmpLimit": 18446744073709551615, ++ "tmpLimitScale": 4294967295, ++ "devShmLimit": 18446744073709551615, ++ "devShmLimitScale": 4294967295 ++} +diff --git a/test/fuzz/fuzz-user-record/edge-min-values.json b/test/fuzz/fuzz-user-record/edge-min-values.json +new file mode 100644 +index 0000000000..b0b569a34e +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/edge-min-values.json +@@ -0,0 +1,41 @@ ++{ ++ "userName": "minuser", ++ "realName": "Minimum Values Test User", ++ "uid": 0, ++ "gid": 0, ++ "homeDirectory": "/", ++ "shell": "/", ++ "disposition": "intrinsic", ++ "umask": 0, ++ "niceLevel": -20, ++ "cpuWeight": 1, ++ "ioWeight": 1, ++ "diskSize": 1, ++ "diskSizeRelative": 1, ++ "tasksMax": 1, ++ "memoryHigh": 1, ++ "memoryMax": 1, ++ "accessMode": 0, ++ "luksPbkdfForceIterations": 1, ++ "luksPbkdfTimeCostUSec": 1, ++ "luksPbkdfMemoryCost": 1, ++ "luksPbkdfParallelThreads": 1, ++ "luksSectorSize": 512, ++ "luksVolumeKeySize": 1, ++ "rebalanceWeight": 1, ++ "rateLimitIntervalUSec": 1, ++ "rateLimitBurst": 1, ++ "stopDelayUSec": 0, ++ "lastChangeUSec": 0, ++ "lastPasswordChangeUSec": 0, ++ "notBeforeUSec": 0, ++ "notAfterUSec": 1, ++ "passwordChangeMinUSec": 0, ++ "passwordChangeMaxUSec": 1, ++ "passwordChangeWarnUSec": 0, ++ "passwordChangeInactiveUSec": 0, ++ "tmpLimit": 1, ++ "tmpLimitScale": 1, ++ "devShmLimit": 1, ++ "devShmLimitScale": 1 ++} +diff --git a/test/fuzz/fuzz-user-record/edge-missing-optionals.json b/test/fuzz/fuzz-user-record/edge-missing-optionals.json +new file mode 100644 +index 0000000000..e06884f976 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/edge-missing-optionals.json +@@ -0,0 +1,9 @@ ++{ ++ "userName": "missingoptionals", ++ "realName": "Missing Optional Fields User", ++ "uid": 3002, ++ "gid": 3002, ++ "homeDirectory": "/home/missingoptionals", ++ "shell": "/bin/bash", ++ "disposition": "regular" ++} +diff --git a/test/fuzz/fuzz-user-record/edge-null-values.json b/test/fuzz/fuzz-user-record/edge-null-values.json +new file mode 100644 +index 0000000000..cb995f38bd +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/edge-null-values.json +@@ -0,0 +1,18 @@ ++{ ++ "userName": "nullvalues", ++ "realName": "Null Values Test User", ++ "uid": 3019, ++ "gid": 3019, ++ "homeDirectory": "/home/nullvalues", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "luks", ++ "autoResizeMode": null, ++ "rebalanceWeight": null, ++ "luksDiscard": null, ++ "luksOfflineDiscard": null, ++ "tmpLimit": null, ++ "tmpLimitScale": null, ++ "devShmLimit": null, ++ "devShmLimitScale": null ++} +diff --git a/test/fuzz/fuzz-user-record/edge-unicode-strings.json b/test/fuzz/fuzz-user-record/edge-unicode-strings.json +new file mode 100644 +index 0000000000..ce1204d080 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/edge-unicode-strings.json +@@ -0,0 +1,21 @@ ++{ ++ "userName": "unicodeuser", ++ "realName": "Ünïcödé Üsér 日本語 中文 العربية", ++ "uid": 3003, ++ "gid": 3003, ++ "homeDirectory": "/home/unicodeuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "emailAddress": "unicode@例え.jp", ++ "location": "東京, 日本 🌸", ++ "environment": [ ++ "GREETING=Привет мир", ++ "HELLO=你好世界" ++ ], ++ "preferredLanguage": "ja_JP.UTF-8", ++ "additionalLanguages": ["zh_CN.UTF-8", "ar_SA.UTF-8", "ru_RU.UTF-8"], ++ "timeZone": "Asia/Tokyo", ++ "privileged": { ++ "passwordHint": "お気に入りの食べ物は何ですか?" ++ } ++} +diff --git a/test/fuzz/fuzz-user-record/full-featured.json b/test/fuzz/fuzz-user-record/full-featured.json +new file mode 100644 +index 0000000000..4ed15a9299 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/full-featured.json +@@ -0,0 +1,245 @@ ++{ ++ "userName": "fuzzuser", ++ "realm": "example.com", ++ "aliases": ["fuzz", "fuzzer", "testfuzz"], ++ "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", ++ "blobDirectory": "/var/cache/systemd/homed/fuzzuser/", ++ "blobManifest": { ++ "avatar": "c0636851d25a62d817ff7da4e081d1e646e42c74d0ecb53425f75fcf1ba43b52", ++ "login-background": "da7ad0222a6edbc6cd095149c72d38d92fd3114f606e4b57469857ef47fade18", ++ "custom-file": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" ++ }, ++ "realName": "Fuzz Test User", ++ "emailAddress": "fuzzuser@example.com", ++ "iconName": "user-available", ++ "location": "Fuzzing Lab, Room 42", ++ "disposition": "regular", ++ "lastChangeUSec": 1700000000000000, ++ "lastPasswordChangeUSec": 1699000000000000, ++ "shell": "/bin/bash", ++ "umask": 22, ++ "environment": [ ++ "EDITOR=vim", ++ "PAGER=less", ++ "LANG=en_US.UTF-8", ++ "FUZZ_VAR=test_value" ++ ], ++ "timeZone": "Europe/Berlin", ++ "preferredLanguage": "en_US.UTF-8", ++ "additionalLanguages": ["de_DE.UTF-8", "fr_FR.UTF-8", "es_ES.UTF-8"], ++ "niceLevel": 5, ++ "resourceLimits": { ++ "RLIMIT_NOFILE": { "cur": 1024, "max": 65536 }, ++ "RLIMIT_NPROC": { "cur": 4096, "max": 8192 }, ++ "RLIMIT_MEMLOCK": { "cur": 65536, "max": 131072 }, ++ "RLIMIT_AS": { "cur": 4294967296, "max": 8589934592 }, ++ "RLIMIT_FSIZE": { "cur": 1073741824, "max": 2147483648 }, ++ "RLIMIT_STACK": { "cur": 8388608, "max": 16777216 }, ++ "RLIMIT_CORE": { "cur": 0, "max": 0 }, ++ "RLIMIT_RSS": { "cur": 4294967296, "max": 8589934592 }, ++ "RLIMIT_CPU": { "cur": 3600, "max": 7200 }, ++ "RLIMIT_DATA": { "cur": 4294967296, "max": 8589934592 }, ++ "RLIMIT_NICE": { "cur": 0, "max": 0 }, ++ "RLIMIT_RTPRIO": { "cur": 0, "max": 0 }, ++ "RLIMIT_RTTIME": { "cur": 1000000, "max": 2000000 }, ++ "RLIMIT_SIGPENDING": { "cur": 128, "max": 256 }, ++ "RLIMIT_MSGQUEUE": { "cur": 819200, "max": 1638400 }, ++ "RLIMIT_LOCKS": { "cur": 1024, "max": 2048 } ++ }, ++ "locked": false, ++ "notBeforeUSec": 1600000000000000, ++ "notAfterUSec": 1900000000000000, ++ "storage": "luks", ++ "diskSize": 107374182400, ++ "diskSizeRelative": 2147483648, ++ "skeletonDirectory": "/etc/skel", ++ "accessMode": 448, ++ "tasksMax": 4096, ++ "memoryHigh": 4294967296, ++ "memoryMax": 8589934592, ++ "cpuWeight": 100, ++ "ioWeight": 100, ++ "mountNoDevices": true, ++ "mountNoSuid": true, ++ "mountNoExecute": false, ++ "cifsDomain": "WORKGROUP", ++ "cifsUserName": "fuzzuser", ++ "cifsService": "//server.example.com/homes/fuzzuser", ++ "cifsExtraMountOptions": "vers=3.0,seal", ++ "imagePath": "/home/fuzzuser.home", ++ "homeDirectory": "/home/fuzzuser", ++ "uid": 60001, ++ "gid": 60001, ++ "memberOf": ["wheel", "users", "audio", "video", "docker", "libvirt"], ++ "capabilityBoundingSet": ["CAP_NET_ADMIN", "CAP_SYS_PTRACE", "CAP_SETUID", "CAP_SETGID"], ++ "capabilityAmbientSet": ["CAP_NET_BIND_SERVICE"], ++ "fileSystemType": "ext4", ++ "partitionUuid": "41f9ce04-c827-4b74-a981-c669f93eb4dc", ++ "luksUuid": "e63581ba-79fb-4226-b9de-1888393f7573", ++ "fileSystemUuid": "758e88c8-5851-4a2a-b88f-e7474279c111", ++ "luksDiscard": true, ++ "luksOfflineDiscard": false, ++ "luksCipher": "aes", ++ "luksCipherMode": "xts-plain64", ++ "luksVolumeKeySize": 64, ++ "luksPbkdfHashAlgorithm": "sha512", ++ "luksPbkdfType": "argon2id", ++ "luksPbkdfForceIterations": 1000, ++ "luksPbkdfTimeCostUSec": 1000000, ++ "luksPbkdfMemoryCost": 1073741824, ++ "luksPbkdfParallelThreads": 4, ++ "luksSectorSize": 4096, ++ "luksExtraMountOptions": "discard,noatime", ++ "dropCaches": true, ++ "autoResizeMode": "shrink-and-grow", ++ "rebalanceWeight": 100, ++ "service": "io.systemd.Home", ++ "rateLimitIntervalUSec": 60000000, ++ "rateLimitBurst": 5, ++ "enforcePasswordPolicy": true, ++ "autoLogin": false, ++ "preferredSessionType": "wayland", ++ "preferredSessionLauncher": "gnome", ++ "stopDelayUSec": 180000000, ++ "killProcesses": true, ++ "passwordChangeMinUSec": 86400000000, ++ "passwordChangeMaxUSec": 7776000000000, ++ "passwordChangeWarnUSec": 1209600000000, ++ "passwordChangeInactiveUSec": 2592000000000, ++ "passwordChangeNow": false, ++ "pkcs11TokenUri": [ ++ "pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=00000000;token=fuzzuser" ++ ], ++ "fido2HmacCredential": [ ++ "AQIDBA==" ++ ], ++ "recoveryKeyType": ["modhex64"], ++ "selfModifiableFields": [ ++ "realName", ++ "emailAddress", ++ "iconName", ++ "location", ++ "preferredLanguage", ++ "additionalLanguages", ++ "timeZone", ++ "preferredSessionType", ++ "preferredSessionLauncher" ++ ], ++ "selfModifiableBlobs": ["avatar", "login-background"], ++ "selfModifiablePrivileged": ["passwordHint"], ++ "tmpLimit": 1073741824, ++ "tmpLimitScale": 858993459, ++ "devShmLimit": 536870912, ++ "devShmLimitScale": 429496729, ++ "defaultArea": "work", ++ "privileged": { ++ "passwordHint": "Your favorite fuzzing target", ++ "hashedPassword": [ ++ "$6$rounds=656000$somesalt$hashedpassworddata1234567890abcdefghijklmnopqrstuvwxyz", ++ "$y$j9T$somesalt$yescrypthash1234567890" ++ ], ++ "sshAuthorizedKeys": [ ++ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG7FVK5YkKL3IYQr7Z6UDYqvB8S8bM7b8vNjVp4S6Y9Y fuzzuser@example.com", ++ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC0123456789abcdef fuzzuser@backup" ++ ], ++ "pkcs11EncryptedKey": [ ++ { ++ "uri": "pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=00000000;token=fuzzuser", ++ "data": "SGVsbG8gV29ybGQhIFRoaXMgaXMgYSB0ZXN0IGVuY3J5cHRlZCBrZXku", ++ "hashedPassword": "$6$rounds=656000$somesalt$encryptedkeyhash" ++ } ++ ], ++ "fido2HmacSalt": [ ++ { ++ "credential": "AQIDBA==", ++ "salt": "c29tZXNhbHRmb3JmaWRvMg==", ++ "hashedPassword": "$6$rounds=656000$fidosalt$fidohashedpassword", ++ "up": true, ++ "uv": true, ++ "clientPin": false ++ } ++ ], ++ "recoveryKey": [ ++ { ++ "type": "modhex64", ++ "hashedPassword": "$6$rounds=656000$recoverysalt$recoverykeyhash" ++ } ++ ] ++ }, ++ "perMachine": [ ++ { ++ "matchMachineId": "15e19cf24e004b949ddaac60c74aa165", ++ "diskSize": 214748364800, ++ "memoryMax": 17179869184, ++ "cpuWeight": 200 ++ }, ++ { ++ "matchHostname": "workstation", ++ "shell": "/bin/zsh", ++ "niceLevel": 0, ++ "autoLogin": true ++ }, ++ { ++ "matchNotMachineId": "00000000000000000000000000000000", ++ "matchNotHostname": "server", ++ "locked": false ++ } ++ ], ++ "binding": { ++ "15e19cf24e004b949ddaac60c74aa165": { ++ "blobDirectory": "/var/cache/systemd/homed/fuzzuser/", ++ "imagePath": "/home/fuzzuser.home", ++ "homeDirectory": "/home/fuzzuser", ++ "partitionUuid": "41f9ce04-c827-4b74-a981-c669f93eb4dc", ++ "luksUuid": "e63581ba-79fb-4226-b9de-1888393f7573", ++ "fileSystemUuid": "758e88c8-5851-4a2a-b88f-e7474279c111", ++ "uid": 60001, ++ "gid": 60001, ++ "storage": "luks", ++ "fileSystemType": "ext4", ++ "luksCipher": "aes", ++ "luksCipherMode": "xts-plain64", ++ "luksVolumeKeySize": 64 ++ } ++ }, ++ "status": { ++ "15e19cf24e004b949ddaac60c74aa165": { ++ "aliases": ["fuzz-status-alias"], ++ "diskUsage": 53687091200, ++ "diskFree": 53687091200, ++ "diskSize": 107374182400, ++ "diskCeiling": 214748364800, ++ "diskFloor": 1073741824, ++ "state": "active", ++ "service": "io.systemd.Home", ++ "signedLocally": true, ++ "goodAuthenticationCounter": 42, ++ "badAuthenticationCounter": 3, ++ "lastGoodAuthenticationUSec": 1700000000000000, ++ "lastBadAuthenticationUSec": 1699999000000000, ++ "rateLimitBeginUSec": 1699999500000000, ++ "rateLimitCount": 1, ++ "removable": false, ++ "accessMode": 448, ++ "fileSystemType": "ext4", ++ "fallbackShell": "/bin/sh", ++ "fallbackHomeDirectory": "/", ++ "useFallback": false, ++ "defaultArea": "work" ++ } ++ }, ++ "signature": [ ++ { ++ "data": "TFUvSGVWclBaU3ppM01KMFBWSHdENW0veGY1MVhEWUNyU3BiRFJOQmR0RjRmRFZock4wdDJJMk9xSC8xeVhpQmlkWGxWMHB0TXVRVnE4S1ZJQ2RFRHE9PQ==", ++ "key": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA/QT6kQWOAMhDJf56jBmszEQQpJHqDsGDMZOdiptBgRk=\n-----END PUBLIC KEY-----\n" ++ } ++ ], ++ "secret": { ++ "password": ["testpassword123", "alternatepassword456"], ++ "tokenPin": ["1234", "5678"], ++ "pkcs11Pin": ["0000"], ++ "pkcs11ProtectedAuthenticationPathPermitted": true, ++ "fido2UserPresencePermitted": true, ++ "fido2UserVerificationPermitted": true ++ } ++} +diff --git a/test/fuzz/fuzz-user-record/luks-complete.json b/test/fuzz/fuzz-user-record/luks-complete.json +new file mode 100644 +index 0000000000..d74b3ae94a +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/luks-complete.json +@@ -0,0 +1,36 @@ ++{ ++ "userName": "luksuser", ++ "realName": "Complete LUKS User", ++ "uid": 3015, ++ "gid": 3015, ++ "homeDirectory": "/home/luksuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "luks", ++ "imagePath": "/home/luksuser.home", ++ "diskSize": 107374182400, ++ "fileSystemType": "btrfs", ++ "partitionUuid": "12345678-1234-1234-1234-123456789abc", ++ "luksUuid": "abcdef12-3456-7890-abcd-ef1234567890", ++ "fileSystemUuid": "fedcba98-7654-3210-fedc-ba9876543210", ++ "luksDiscard": true, ++ "luksOfflineDiscard": true, ++ "luksCipher": "aes", ++ "luksCipherMode": "xts-plain64", ++ "luksVolumeKeySize": 64, ++ "luksPbkdfHashAlgorithm": "sha256", ++ "luksPbkdfType": "pbkdf2", ++ "luksPbkdfForceIterations": 100000, ++ "luksPbkdfTimeCostUSec": 500000, ++ "luksPbkdfMemoryCost": 67108864, ++ "luksPbkdfParallelThreads": 2, ++ "luksSectorSize": 4096, ++ "luksExtraMountOptions": "compress=zstd:3,noatime", ++ "dropCaches": true, ++ "mountNoDevices": true, ++ "mountNoSuid": true, ++ "mountNoExecute": false, ++ "accessMode": 448, ++ "autoResizeMode": "shrink-and-grow", ++ "rebalanceWeight": 100 ++} +diff --git a/test/fuzz/fuzz-user-record/minimal.json b/test/fuzz/fuzz-user-record/minimal.json +new file mode 100644 +index 0000000000..a22cc44fb3 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/minimal.json +@@ -0,0 +1,3 @@ ++{ ++ "userName": "u" ++} +diff --git a/test/fuzz/fuzz-user-record/password-policy.json b/test/fuzz/fuzz-user-record/password-policy.json +new file mode 100644 +index 0000000000..c4b336a4f5 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/password-policy.json +@@ -0,0 +1,24 @@ ++{ ++ "userName": "policuser", ++ "realName": "Password Policy User", ++ "uid": 3017, ++ "gid": 3017, ++ "homeDirectory": "/home/policyuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "enforcePasswordPolicy": true, ++ "passwordChangeNow": true, ++ "passwordChangeMinUSec": 86400000000, ++ "passwordChangeMaxUSec": 7776000000000, ++ "passwordChangeWarnUSec": 1209600000000, ++ "passwordChangeInactiveUSec": 2592000000000, ++ "lastPasswordChangeUSec": 1700000000000000, ++ "rateLimitIntervalUSec": 60000000, ++ "rateLimitBurst": 3, ++ "privileged": { ++ "hashedPassword": [ ++ "$6$rounds=656000$salt$hashedpassword" ++ ], ++ "passwordHint": "Your first pet's name" ++ } ++} +diff --git a/test/fuzz/fuzz-user-record/per-machine-complex.json b/test/fuzz/fuzz-user-record/per-machine-complex.json +new file mode 100644 +index 0000000000..327f3e5a92 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/per-machine-complex.json +@@ -0,0 +1,58 @@ ++{ ++ "userName": "permachineuser", ++ "realName": "Per Machine Complex User", ++ "uid": 3005, ++ "gid": 3005, ++ "homeDirectory": "/home/permachineuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "luks", ++ "diskSize": 53687091200, ++ "memoryMax": 4294967296, ++ "cpuWeight": 100, ++ "perMachine": [ ++ { ++ "matchMachineId": "11111111111111111111111111111111", ++ "diskSize": 107374182400, ++ "memoryMax": 8589934592, ++ "cpuWeight": 200, ++ "storage": "luks", ++ "shell": "/bin/zsh" ++ }, ++ { ++ "matchMachineId": ["22222222222222222222222222222222", "33333333333333333333333333333333"], ++ "diskSize": 214748364800, ++ "memoryMax": 17179869184, ++ "ioWeight": 500 ++ }, ++ { ++ "matchHostname": "workstation", ++ "autoLogin": true, ++ "niceLevel": -5, ++ "environment": ["WORKSTATION=true"] ++ }, ++ { ++ "matchHostname": ["server1", "server2"], ++ "locked": false, ++ "killProcesses": false, ++ "stopDelayUSec": 300000000 ++ }, ++ { ++ "matchNotMachineId": "44444444444444444444444444444444", ++ "matchNotHostname": "restricted-host", ++ "tasksMax": 8192 ++ }, ++ { ++ "matchMachineId": "55555555555555555555555555555555", ++ "matchHostname": "special-host", ++ "blobDirectory": "/var/cache/special/blobs/", ++ "blobManifest": { ++ "special-file": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" ++ }, ++ "selfModifiableFields": ["realName", "location"], ++ "selfModifiableBlobs": ["avatar"], ++ "preferredSessionType": "x11", ++ "preferredSessionLauncher": "plasma" ++ } ++ ] ++} +diff --git a/test/fuzz/fuzz-user-record/rlimits-all.json b/test/fuzz/fuzz-user-record/rlimits-all.json +new file mode 100644 +index 0000000000..ea9299a69e +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/rlimits-all.json +@@ -0,0 +1,27 @@ ++{ ++ "userName": "rlimitsuser", ++ "realName": "Resource Limits Test User", ++ "uid": 3009, ++ "gid": 3009, ++ "homeDirectory": "/home/rlimitsuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "resourceLimits": { ++ "RLIMIT_AS": { "cur": 4294967296, "max": 8589934592 }, ++ "RLIMIT_CORE": { "cur": 0, "max": 9223372036854775807 }, ++ "RLIMIT_CPU": { "cur": 3600, "max": 9223372036854775807 }, ++ "RLIMIT_DATA": { "cur": 4294967296, "max": 9223372036854775807 }, ++ "RLIMIT_FSIZE": { "cur": 1073741824, "max": 9223372036854775807 }, ++ "RLIMIT_LOCKS": { "cur": 1024, "max": 9223372036854775807 }, ++ "RLIMIT_MEMLOCK": { "cur": 65536, "max": 9223372036854775807 }, ++ "RLIMIT_MSGQUEUE": { "cur": 819200, "max": 9223372036854775807 }, ++ "RLIMIT_NICE": { "cur": 0, "max": 40 }, ++ "RLIMIT_NOFILE": { "cur": 1024, "max": 1048576 }, ++ "RLIMIT_NPROC": { "cur": 4096, "max": 9223372036854775807 }, ++ "RLIMIT_RSS": { "cur": 4294967296, "max": 9223372036854775807 }, ++ "RLIMIT_RTPRIO": { "cur": 0, "max": 99 }, ++ "RLIMIT_RTTIME": { "cur": 1000000, "max": 9223372036854775807 }, ++ "RLIMIT_SIGPENDING": { "cur": 128, "max": 9223372036854775807 }, ++ "RLIMIT_STACK": { "cur": 8388608, "max": 9223372036854775807 } ++ } ++} +diff --git a/test/fuzz/fuzz-user-record/session-prefs.json b/test/fuzz/fuzz-user-record/session-prefs.json +new file mode 100644 +index 0000000000..56344241f3 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/session-prefs.json +@@ -0,0 +1,14 @@ ++{ ++ "userName": "sessionuser", ++ "realName": "Session Preferences User", ++ "uid": 3016, ++ "gid": 3016, ++ "homeDirectory": "/home/sessionuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "autoLogin": true, ++ "preferredSessionType": "wayland", ++ "preferredSessionLauncher": "gnome", ++ "stopDelayUSec": 180000000, ++ "killProcesses": false ++} +diff --git a/test/fuzz/fuzz-user-record/ssh-keys.json b/test/fuzz/fuzz-user-record/ssh-keys.json +new file mode 100644 +index 0000000000..00d947d6ba +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/ssh-keys.json +@@ -0,0 +1,18 @@ ++{ ++ "userName": "sshuser", ++ "realName": "SSH Keys User", ++ "uid": 3013, ++ "gid": 3013, ++ "homeDirectory": "/home/sshuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "privileged": { ++ "sshAuthorizedKeys": [ ++ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKlH9A7KdFhMmfBrV2fzONpPeaQaJAXyY3bMpZ1sT5Xy ed25519-key", ++ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC7s... rsa-key", ++ "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBA... ecdsa-key", ++ "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIA... security-key", ++ "command=\"/usr/bin/restricted\",no-port-forwarding,no-agent-forwarding ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJx... restricted-key" ++ ] ++ } ++} +diff --git a/test/fuzz/fuzz-user-record/storage-cifs.json b/test/fuzz/fuzz-user-record/storage-cifs.json +new file mode 100644 +index 0000000000..f30d5d676d +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/storage-cifs.json +@@ -0,0 +1,15 @@ ++{ ++ "userName": "cifsuser", ++ "realName": "CIFS Home User", ++ "uid": 1001, ++ "gid": 1001, ++ "homeDirectory": "/home/cifsuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "cifs", ++ "cifsDomain": "EXAMPLE", ++ "cifsUserName": "cifsuser", ++ "cifsService": "//fileserver.example.com/homes/cifsuser", ++ "cifsExtraMountOptions": "vers=3.0,seal,sec=krb5", ++ "memberOf": ["users", "domain-users"] ++} +diff --git a/test/fuzz/fuzz-user-record/storage-classic.json b/test/fuzz/fuzz-user-record/storage-classic.json +new file mode 100644 +index 0000000000..b77e07d09b +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/storage-classic.json +@@ -0,0 +1,10 @@ ++{ ++ "userName": "classicuser", ++ "realName": "Classic Unix User", ++ "uid": 1005, ++ "gid": 1005, ++ "homeDirectory": "/home/classicuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "classic" ++} +diff --git a/test/fuzz/fuzz-user-record/storage-directory.json b/test/fuzz/fuzz-user-record/storage-directory.json +new file mode 100644 +index 0000000000..12dbd6eb5e +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/storage-directory.json +@@ -0,0 +1,13 @@ ++{ ++ "userName": "diruser", ++ "realName": "Directory Storage User", ++ "uid": 1002, ++ "gid": 1002, ++ "homeDirectory": "/home/diruser", ++ "imagePath": "/home/diruser.homedir", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "directory", ++ "accessMode": 448, ++ "diskSize": 10737418240 ++} +diff --git a/test/fuzz/fuzz-user-record/storage-fscrypt.json b/test/fuzz/fuzz-user-record/storage-fscrypt.json +new file mode 100644 +index 0000000000..63c2887fd4 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/storage-fscrypt.json +@@ -0,0 +1,14 @@ ++{ ++ "userName": "fscryptuser", ++ "realName": "Fscrypt User", ++ "uid": 1004, ++ "gid": 1004, ++ "homeDirectory": "/home/fscryptuser", ++ "imagePath": "/home/fscryptuser.homedir", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "fscrypt", ++ "fileSystemType": "ext4", ++ "accessMode": 448, ++ "diskSize": 21474836480 ++} +diff --git a/test/fuzz/fuzz-user-record/storage-subvolume.json b/test/fuzz/fuzz-user-record/storage-subvolume.json +new file mode 100644 +index 0000000000..8e80145d5c +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/storage-subvolume.json +@@ -0,0 +1,14 @@ ++{ ++ "userName": "btrfsuser", ++ "realName": "Btrfs Subvolume User", ++ "uid": 1003, ++ "gid": 1003, ++ "homeDirectory": "/home/btrfsuser", ++ "imagePath": "/home/btrfsuser.homedir", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "storage": "subvolume", ++ "fileSystemType": "btrfs", ++ "accessMode": 448, ++ "diskSize": 53687091200 ++} +diff --git a/test/fuzz/fuzz-user-record/time-constraints.json b/test/fuzz/fuzz-user-record/time-constraints.json +new file mode 100644 +index 0000000000..238efa3e69 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/time-constraints.json +@@ -0,0 +1,13 @@ ++{ ++ "userName": "timeuser", ++ "realName": "Time Constraints User", ++ "uid": 3018, ++ "gid": 3018, ++ "homeDirectory": "/home/timeuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "notBeforeUSec": 1609459200000000, ++ "notAfterUSec": 1924905600000000, ++ "lastChangeUSec": 1700000000000000, ++ "locked": false ++} +diff --git a/test/fuzz/fuzz-user-record/tmpfs-limits.json b/test/fuzz/fuzz-user-record/tmpfs-limits.json +new file mode 100644 +index 0000000000..725e6df8a2 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/tmpfs-limits.json +@@ -0,0 +1,13 @@ ++{ ++ "userName": "tmpfsuser", ++ "realName": "Tmpfs Limits Test User", ++ "uid": 3014, ++ "gid": 3014, ++ "homeDirectory": "/home/tmpfsuser", ++ "shell": "/bin/bash", ++ "disposition": "regular", ++ "tmpLimit": 1073741824, ++ "tmpLimitScale": 214748364, ++ "devShmLimit": 536870912, ++ "devShmLimitScale": 107374182 ++} +diff --git a/test/fuzz/fuzz-user-record/with-realm.json b/test/fuzz/fuzz-user-record/with-realm.json +new file mode 100644 +index 0000000000..ab77c824af +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/with-realm.json +@@ -0,0 +1,10 @@ ++{ ++ "userName": "realmuser", ++ "realm": "corp.example.com", ++ "realName": "Corporate Realm User", ++ "uid": 3011, ++ "gid": 3011, ++ "homeDirectory": "/home/realmuser@corp.example.com", ++ "shell": "/bin/bash", ++ "disposition": "regular" ++} diff --git a/0677-user-record-extract-user_record_image_is_blockdev-co.patch b/0677-user-record-extract-user_record_image_is_blockdev-co.patch new file mode 100644 index 0000000..fa82ca3 --- /dev/null +++ b/0677-user-record-extract-user_record_image_is_blockdev-co.patch @@ -0,0 +1,81 @@ +From fc1337e72cc8c224ddf0e99297b1db6f15be622a Mon Sep 17 00:00:00 2001 +From: Mike Yuan +Date: Fri, 6 Mar 2026 22:07:31 +0100 +Subject: [PATCH] user-record: extract user_record_image_is_blockdev() common + helper + +(cherry picked from commit a7f1670f62cc8bc37f52acee94d2209eff66cd10) + +Related: RHEL-155021 +--- + src/shared/user-record.c | 20 +++++++++++-------- + .../crash-empty-image-path.json | 4 ++++ + 2 files changed, 16 insertions(+), 8 deletions(-) + create mode 100644 test/fuzz/fuzz-user-record/crash-empty-image-path.json + +diff --git a/src/shared/user-record.c b/src/shared/user-record.c +index f4febcdebe..df65df72fa 100644 +--- a/src/shared/user-record.c ++++ b/src/shared/user-record.c +@@ -1856,6 +1856,16 @@ const char* user_record_image_path(UserRecord *h) { + user_record_home_directory_real(h) : NULL; + } + ++static bool user_record_image_is_blockdev(UserRecord *h) { ++ assert(h); ++ ++ const char *p = user_record_image_path(h); ++ if (!p) ++ return false; ++ ++ return path_startswith(p, "/dev/"); ++} ++ + const char* user_record_cifs_user_name(UserRecord *h) { + assert(h); + +@@ -1907,24 +1917,18 @@ const char* user_record_real_name(UserRecord *h) { + } + + bool user_record_luks_discard(UserRecord *h) { +- const char *ip; +- + assert(h); + + if (h->luks_discard >= 0) + return h->luks_discard; + +- ip = user_record_image_path(h); +- if (!ip) +- return false; +- + /* Use discard by default if we are referring to a real block device, but not when operating on a + * loopback device. We want to optimize for SSD and flash storage after all, but we should be careful + * when storing stuff on top of regular file systems in loopback files as doing discard then would + * mean thin provisioning and we should not do that willy-nilly since it means we'll risk EIO later + * on should the disk space to back our file systems not be available. */ + +- return path_startswith(ip, "/dev/"); ++ return user_record_image_is_blockdev(h); + } + + bool user_record_luks_offline_discard(UserRecord *h) { +@@ -2090,7 +2094,7 @@ int user_record_removable(UserRecord *h) { + return -1; + + /* For now consider only LUKS home directories with a reference by path as removable */ +- return storage == USER_LUKS && path_startswith(user_record_image_path(h), "/dev/"); ++ return storage == USER_LUKS && user_record_image_is_blockdev(h); + } + + uint64_t user_record_ratelimit_interval_usec(UserRecord *h) { +diff --git a/test/fuzz/fuzz-user-record/crash-empty-image-path.json b/test/fuzz/fuzz-user-record/crash-empty-image-path.json +new file mode 100644 +index 0000000000..0506a71fc2 +--- /dev/null ++++ b/test/fuzz/fuzz-user-record/crash-empty-image-path.json +@@ -0,0 +1,4 @@ ++{ ++ "userName": "root", ++ "storage": "luks" ++} diff --git a/0678-udev-net_id-introduce-naming-scheme-for-RHEL-9.9.patch b/0678-udev-net_id-introduce-naming-scheme-for-RHEL-9.9.patch new file mode 100644 index 0000000..2b59e10 --- /dev/null +++ b/0678-udev-net_id-introduce-naming-scheme-for-RHEL-9.9.patch @@ -0,0 +1,58 @@ +From 8f011c16cc4fffd1c94211a081f571554bb19103 Mon Sep 17 00:00:00 2001 +From: Jan Macku +Date: Mon, 4 May 2026 10:00:28 +0200 +Subject: [PATCH] udev/net_id: introduce naming scheme for RHEL-9.9 + +rhel-only: policy + +Resolves: RHEL-72814 +--- + man/systemd.net-naming-scheme.xml | 9 +++++++++ + src/shared/netif-naming-scheme.c | 1 + + src/shared/netif-naming-scheme.h | 1 + + 3 files changed, 11 insertions(+) + +diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml +index 3c4d67cce3..623296a381 100644 +--- a/man/systemd.net-naming-scheme.xml ++++ b/man/systemd.net-naming-scheme.xml +@@ -679,6 +679,15 @@ + + + ++ ++ ++ rhel-9.9 ++ ++ Same as naming scheme rhel-9.8. ++ ++ ++ ++ + + + +diff --git a/src/shared/netif-naming-scheme.c b/src/shared/netif-naming-scheme.c +index 18b012b8c8..00f1e2960f 100644 +--- a/src/shared/netif-naming-scheme.c ++++ b/src/shared/netif-naming-scheme.c +@@ -45,6 +45,7 @@ static const NamingScheme naming_schemes[] = { + { "rhel-9.6", NAMING_RHEL_9_6 }, + { "rhel-9.7", NAMING_RHEL_9_7 }, + { "rhel-9.8", NAMING_RHEL_9_8 }, ++ { "rhel-9.9", NAMING_RHEL_9_9 }, + { "rhel-10.0-beta", NAMING_RHEL_10_0_BETA }, + { "rhel-10.0", NAMING_RHEL_10_0 }, + { "rhel-10.1", NAMING_RHEL_10_1 }, +diff --git a/src/shared/netif-naming-scheme.h b/src/shared/netif-naming-scheme.h +index eab374fb9a..ec3bd96114 100644 +--- a/src/shared/netif-naming-scheme.h ++++ b/src/shared/netif-naming-scheme.h +@@ -87,6 +87,7 @@ typedef enum NamingSchemeFlags { + NAMING_RHEL_9_6 = NAMING_RHEL_9_5, + NAMING_RHEL_9_7 = NAMING_RHEL_9_5, + NAMING_RHEL_9_8 = NAMING_RHEL_9_5 | NAMING_FIRMWARE_NODE_SUN, ++ NAMING_RHEL_9_9 = NAMING_RHEL_9_8, + + NAMING_RHEL_10_0_BETA = NAMING_V255, + NAMING_RHEL_10_0 = NAMING_V257, diff --git a/0679-udev-net_id-introduce-naming-scheme-for-RHEL-10.3.patch b/0679-udev-net_id-introduce-naming-scheme-for-RHEL-10.3.patch new file mode 100644 index 0000000..caf6343 --- /dev/null +++ b/0679-udev-net_id-introduce-naming-scheme-for-RHEL-10.3.patch @@ -0,0 +1,58 @@ +From f7ad5e9f0e96751e7e8084ecf23813bababc2def Mon Sep 17 00:00:00 2001 +From: Jan Macku +Date: Mon, 4 May 2026 10:02:45 +0200 +Subject: [PATCH] udev/net_id: introduce naming scheme for RHEL-10.3 + +rhel-only: policy + +Resolves: RHEL-72814 +--- + man/systemd.net-naming-scheme.xml | 9 +++++++++ + src/shared/netif-naming-scheme.c | 1 + + src/shared/netif-naming-scheme.h | 1 + + 3 files changed, 11 insertions(+) + +diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml +index 623296a381..19ad5c2340 100644 +--- a/man/systemd.net-naming-scheme.xml ++++ b/man/systemd.net-naming-scheme.xml +@@ -576,6 +576,15 @@ + + + ++ ++ ++ rhel-10.3 ++ ++ Same as naming scheme rhel-10.0. ++ ++ ++ ++ + + By default rhel-10.0 is used. + +diff --git a/src/shared/netif-naming-scheme.c b/src/shared/netif-naming-scheme.c +index 00f1e2960f..2faa57f1f5 100644 +--- a/src/shared/netif-naming-scheme.c ++++ b/src/shared/netif-naming-scheme.c +@@ -50,6 +50,7 @@ static const NamingScheme naming_schemes[] = { + { "rhel-10.0", NAMING_RHEL_10_0 }, + { "rhel-10.1", NAMING_RHEL_10_1 }, + { "rhel-10.2", NAMING_RHEL_10_2 }, ++ { "rhel-10.3", NAMING_RHEL_10_3 }, + /* … add more schemes here, as the logic to name devices is updated … */ + + EXTRA_NET_NAMING_MAP +diff --git a/src/shared/netif-naming-scheme.h b/src/shared/netif-naming-scheme.h +index ec3bd96114..8fae83f61a 100644 +--- a/src/shared/netif-naming-scheme.h ++++ b/src/shared/netif-naming-scheme.h +@@ -93,6 +93,7 @@ typedef enum NamingSchemeFlags { + NAMING_RHEL_10_0 = NAMING_V257, + NAMING_RHEL_10_1 = NAMING_RHEL_10_0, + NAMING_RHEL_10_2 = NAMING_RHEL_10_0, ++ NAMING_RHEL_10_3 = NAMING_RHEL_10_0, + + EXTRA_NET_NAMING_SCHEMES + diff --git a/0680-Tag-accel-devices-for-uaccess-render.patch b/0680-Tag-accel-devices-for-uaccess-render.patch new file mode 100644 index 0000000..4e40468 --- /dev/null +++ b/0680-Tag-accel-devices-for-uaccess-render.patch @@ -0,0 +1,28 @@ +From db3cce5b588ff3f005fa3757101c328a83362dd3 Mon Sep 17 00:00:00 2001 +From: "Mario Limonciello (AMD)" +Date: Sun, 15 Feb 2026 07:28:47 -0600 +Subject: [PATCH] Tag accel devices for uaccess-render + +accel devices are used for things like NPUs and should be tagged +for the logged in user just like GPUs are. + +(cherry picked from commit e30c044c23c1dc7ef44ccb3892d942dc256d1b02) + +Resolves: RHEL-153030 +--- + rules.d/70-uaccess.rules.in | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/rules.d/70-uaccess.rules.in b/rules.d/70-uaccess.rules.in +index 046f169e44..6ba4200ff9 100644 +--- a/rules.d/70-uaccess.rules.in ++++ b/rules.d/70-uaccess.rules.in +@@ -47,6 +47,8 @@ SUBSYSTEM=="drm", KERNEL=="card*", TAG+="uaccess" + {% if GROUP_RENDER_UACCESS %} + # DRI render nodes + SUBSYSTEM=="drm", KERNEL=="renderD*", TAG+="uaccess" ++# DRI accel nodes ++SUBSYSTEM=="accel", KERNEL=="accel*", TAG+="uaccess", TAG+="xaccess-accel" + {% endif %} + {% if DEV_KVM_UACCESS %} + # KVM diff --git a/0681-udev-tag-kfd-devices-for-xaccess-render-40888.patch b/0681-udev-tag-kfd-devices-for-xaccess-render-40888.patch new file mode 100644 index 0000000..0493812 --- /dev/null +++ b/0681-udev-tag-kfd-devices-for-xaccess-render-40888.patch @@ -0,0 +1,29 @@ +From 9041d136f9d72358233b9b66eef13a7906e1c794 Mon Sep 17 00:00:00 2001 +From: Mario Limonciello +Date: Sun, 1 Mar 2026 21:32:50 -0600 +Subject: [PATCH] udev: tag kfd devices for xaccess-render (#40888) + +The kfd device is used for running compute workloads on AMD +GPUs. Users that are logged in should be able to run compute +so tag them like other DRM and ACCEL devices are. + +(cherry picked from commit 9d3b73d9bf181e6adf2e900739c4d998e3450a12) + +Resolves: RHEL-153030 +--- + rules.d/70-uaccess.rules.in | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/rules.d/70-uaccess.rules.in b/rules.d/70-uaccess.rules.in +index 6ba4200ff9..8c05bd3454 100644 +--- a/rules.d/70-uaccess.rules.in ++++ b/rules.d/70-uaccess.rules.in +@@ -49,6 +49,8 @@ SUBSYSTEM=="drm", KERNEL=="card*", TAG+="uaccess" + SUBSYSTEM=="drm", KERNEL=="renderD*", TAG+="uaccess" + # DRI accel nodes + SUBSYSTEM=="accel", KERNEL=="accel*", TAG+="uaccess", TAG+="xaccess-accel" ++# AMD KFD nodes ++SUBSYSTEM=="kfd", KERNEL=="kfd", TAG+="uaccess", TAG+="xaccess-render" + {% endif %} + {% if DEV_KVM_UACCESS %} + # KVM diff --git a/systemd.spec b/systemd.spec index ad0e811..78782ff 100644 --- a/systemd.spec +++ b/systemd.spec @@ -48,7 +48,7 @@ Url: https://systemd.io # Allow users to specify the version and release when building the rpm by # setting the %%version_override and %%release_override macros. Version: %{?version_override}%{!?version_override:257} -Release: 24%{?dist} +Release: 25%{?dist} %global stable %(c="%version"; [ "$c" = "${c#*.*}" ]; echo $?) @@ -771,6 +771,26 @@ Patch0658: 0658-user-runtime-dir-correct-quota-size-calculation.patch Patch0659: 0659-test-display-quota-add-a-little-helper-binary-to-sho.patch Patch0660: 0660-TEST-46-HOMED-check-for-support-on-dev-shm-and-tmp-s.patch Patch0661: 0661-TEST-46-HOMED-conditionally-skip-usrquota-tests.patch +Patch0662: 0662-core-cleanup-unit-s-dropin-directories-from-global-c.patch +Patch0663: 0663-libsystemd-drop-const-decorators-on-public-inline-fu.patch +Patch0664: 0664-sd-id128-Drop-_sd_const_-from-sd_id128_in_setv.patch +Patch0665: 0665-test-wrap-mount-umount-when-running-with-sanitizers.patch +Patch0666: 0666-test-wrap-even-more-binaries-when-running-with-sanit.patch +Patch0667: 0667-test-temporarily-ignore-sanitizer-warning-about-bloc.patch +Patch0668: 0668-test-slightly-reduce-the-performance-memory-overhead.patch +Patch0669: 0669-fstab-generator-support-swap-on-network-block-device.patch +Patch0670: 0670-man-add-tags-for-the-next-few-versions.patch +Patch0671: 0671-test-drop-some-extraneous-whitespaces.patch +Patch0672: 0672-shared-fix-segfault-when-processing-matchHostname-fi.patch +Patch0673: 0673-shared-don-t-exclude-valid-min-max-values-for-cgroup.patch +Patch0674: 0674-shared-don-t-leak-memory-from-array-fields.patch +Patch0675: 0675-man-fix-short-option-for-userdbctl-s-from-file.patch +Patch0676: 0676-fuzz-add-a-fuzzer-for-user-records.patch +Patch0677: 0677-user-record-extract-user_record_image_is_blockdev-co.patch +Patch0678: 0678-udev-net_id-introduce-naming-scheme-for-RHEL-9.9.patch +Patch0679: 0679-udev-net_id-introduce-naming-scheme-for-RHEL-10.3.patch +Patch0680: 0680-Tag-accel-devices-for-uaccess-render.patch +Patch0681: 0681-udev-tag-kfd-devices-for-xaccess-render-40888.patch # Downstream-only patches (9000–9999) %endif @@ -1722,6 +1742,28 @@ rm -f .file-list-* rm -f %{name}.lang %changelog +* Tue May 12 2026 systemd maintenance team - 257-25 +- core: cleanup unit's dropin directories from global cache (RHEL-171097) +- libsystemd: drop "const" decorators on public inline functions (RHEL-155454) +- sd-id128: Drop _sd_const_ from sd_id128_in_setv() (RHEL-155454) +- test: wrap mount/umount when running with sanitizers (RHEL-155454) +- test: wrap even more binaries when running with sanitizers (RHEL-155454) +- test: temporarily ignore sanitizer warning about blocked ptrace() (RHEL-155454) +- test: slightly reduce the performance/memory overhead for wrapped binaries (RHEL-155454) +- fstab-generator: support swap on network block devices (RHEL-128058) +- man: add tags for the next few versions (RHEL-128058) +- test: drop some extraneous whitespaces (RHEL-155021) +- shared: fix segfault when processing matchHostname field (RHEL-155021) +- shared: don't exclude valid min/max values for cgroup weight fields (RHEL-155021) +- shared: don't leak memory from array fields (RHEL-155021) +- man: fix short option for userdbctl's --from-file= (RHEL-155021) +- fuzz: add a fuzzer for user records (RHEL-155021) +- user-record: extract user_record_image_is_blockdev() common helper (RHEL-155021) +- udev/net_id: introduce naming scheme for RHEL-9.9 (RHEL-72814) +- udev/net_id: introduce naming scheme for RHEL-10.3 (RHEL-72814) +- Tag accel devices for uaccess-render (RHEL-153030) +- udev: tag kfd devices for xaccess-render (#40888) (RHEL-153030) + * Thu Apr 16 2026 systemd maintenance team - 257-24 - ci: re-enable bpf-framework option for build and unit test jobs (RHEL-155454) - ci: add bpftool workaround to codeql job too (RHEL-155454)