From 5846c2b259b469d3841579b6172cda3115e4cea0 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Thu, 9 Apr 2026 18:11:33 +0200 Subject: [PATCH 01/29] portablectl: fix swapped arguments for setns() Follow-up for 824fcb95c9e66abe6b350ebab6e0593498ff7aa1. (cherry picked from commit 44d0f273fa9d237c73b80b110a67da5045822796) --- src/portable/portable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/portable/portable.c b/src/portable/portable.c index a7f3ce9dfd052..1bbdfc573969a 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -519,7 +519,7 @@ static int portable_extract_by_path( seq[0] = safe_close(seq[0]); errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); - if (setns(CLONE_NEWUSER, userns_fd) < 0) { + if (setns(userns_fd, CLONE_NEWUSER) < 0) { r = log_debug_errno(errno, "Failed to join userns: %m"); report_errno_and_exit(errno_pipe_fd[1], r); } From bb99bb13f8e07ac60c768bf57d3c0ec2744de15b Mon Sep 17 00:00:00 2001 From: Milan Kyselica Date: Thu, 9 Apr 2026 19:45:19 +0200 Subject: [PATCH 02/29] udev: fix bounds check in dev_if_packed_info() The check compared bLength against (size - sizeof(descriptor)), which is an absolute limit unrelated to the current buffer position. Since bLength is uint8_t (max 255), this can never exceed size - 9 for any realistic input, making the check dead code. Use (size - pos) instead so the check actually catches descriptors that extend past the end of the read data. Fixes: https://github.com/systemd/systemd/issues/41570 (cherry picked from commit 4b32ab5a36aea7752be26c18dabc3a554189b19d) --- src/udev/udev-builtin-usb_id.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c index 80597ea89ee25..61250b7072fe0 100644 --- a/src/udev/udev-builtin-usb_id.c +++ b/src/udev/udev-builtin-usb_id.c @@ -168,7 +168,7 @@ static int dev_if_packed_info(sd_device *dev, char *ifs_str, size_t len) { desc = (struct usb_interface_descriptor *) (buf + pos); if (desc->bLength < 3) break; - if (desc->bLength > size - sizeof(struct usb_interface_descriptor)) + if (desc->bLength > (size_t) size - pos) return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EIO), "Corrupt data read from \"%s\"", filename); pos += desc->bLength; From 20200136f712678b7f58953951d5de90313138fd Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Thu, 9 Apr 2026 15:12:48 +0200 Subject: [PATCH 03/29] sd-varlink: fix a potential connection count leak With the old version there was a potential connection count leak if either of the two hashmap operations in count_connection() failed. In that case we'd return from sd_varlink_server_add_connection_pair() _before_ attached the sd_varlink_server object to an sd_varlink object, and since varlink_detach_server() is the only place where the connection counter is decremented (called through sd_varlink_close() in various error paths later _if_ the "server" object is not null, i.e. attached to the sd_varlink object) we'd "leak" a connection every time this happened. However, the potential of abusing this is very theoretical, as one would need to hit OOM every time either of the hashmap operations was executed for a while before exhausting the connection limit. Let's just increment the connection counter after any potential error path, so we don't have to deal with potential rollbacks. (cherry picked from commit d3a1710bc22b9047620d1a05f76dee8590255206) --- src/libsystemd/sd-varlink/sd-varlink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsystemd/sd-varlink/sd-varlink.c b/src/libsystemd/sd-varlink/sd-varlink.c index 7ee85e9789542..8e8fc03a030e5 100644 --- a/src/libsystemd/sd-varlink/sd-varlink.c +++ b/src/libsystemd/sd-varlink/sd-varlink.c @@ -3562,8 +3562,6 @@ static int count_connection(sd_varlink_server *server, const struct ucred *ucred assert(server); assert(ucred); - server->n_connections++; - if (FLAGS_SET(server->flags, SD_VARLINK_SERVER_ACCOUNT_UID)) { assert(uid_is_valid(ucred->uid)); @@ -3581,6 +3579,8 @@ static int count_connection(sd_varlink_server *server, const struct ucred *ucred return varlink_server_log_errno(server, r, "Failed to increment counter in UID hash table: %m"); } + server->n_connections++; + return 0; } From ca10b4e0fa27d88e8bf76b97ac7ec6b82e1234e0 Mon Sep 17 00:00:00 2001 From: azureuser Date: Tue, 3 Mar 2026 08:41:45 +0000 Subject: [PATCH 04/29] resolved: skip cache flush on server switch/re-probe when StaleRetentionSec is set manager_set_dns_server() and dns_server_flush_cache() call dns_cache_flush() unconditionally, wiping the entire cache even when StaleRetentionSec is configured. This defeats serve-stale by discarding cached records that should remain available during server switches and feature-level re-probes. The original serve-stale commit (5ed91481ab) added a stale_retention_usec guard to link_set_dns_server(), and a later commit (7928c0e0a1) added the same guard to dns_delegate_set_dns_server(), but these two call sites in resolved-dns-server.c were missed. This is particularly visible with DNSOverTLS, where TLS handshake failures trigger frequent feature-level downgrades and re-probes via dns_server_flush_cache(), flushing the cache each time. Add the same stale_retention_usec guard to both call sites so that cache entries are allowed to expire naturally via dns_cache_prune() when serve-stale is enabled. Fixes: #40781 This commit was prepared with assistance from an AI coding agent (GitHub Copilot). All changes have been reviewed for correctness and adherence to the systemd coding style. (cherry picked from commit fb0ae7436dbd7182ec2d53cca366599f9031db4d) --- src/resolve/resolved-dns-server.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 4f04476f2d4ac..6d1b349c4900a 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -1036,7 +1036,8 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) { dns_server_unref(m->current_dns_server); m->current_dns_server = dns_server_ref(s); - if (m->unicast_scope) + /* Skip flushing the cache if server stale feature is enabled. */ + if (m->unicast_scope && m->stale_retention_usec == 0) dns_cache_flush(&m->unicast_scope->cache); (void) manager_send_changed(m, "CurrentDNSServer"); @@ -1155,6 +1156,10 @@ void dns_server_flush_cache(DnsServer *s) { if (!scope) return; + /* Skip flushing the cache if server stale feature is enabled. */ + if (s->manager->stale_retention_usec > 0) + return; + dns_cache_flush(&scope->cache); } From aebf1b8e4c67a7e2ada8f5e39e7622f67dd64a61 Mon Sep 17 00:00:00 2001 From: Milan Kyselica Date: Thu, 9 Apr 2026 19:43:14 +0200 Subject: [PATCH 05/29] resolved: replace assert() with error return in DNSSEC verify functions dnssec_rsa_verify_raw() asserts that RSA_size(key) matches the RRSIG signature size, and dnssec_ecdsa_verify_raw() asserts that EC_KEY_check_key() succeeds. Both conditions depend on parsed DNS record content. Replace with proper error returns. The actual crypto verify calls (EVP_PKEY_verify / ECDSA_do_verify) handle mismatches fine on their own, so the asserts were also redundant. While at it, fix the misleading "EC_POINT_bn2point failed" log message that actually refers to an EC_KEY_set_public_key() failure. Fixes: https://github.com/systemd/systemd/issues/41569 (cherry picked from commit dd80e5a348bdb8185e040f66ede00fd4ffdee777) --- src/resolve/resolved-dns-dnssec.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index ced874e2ba9f2..e75ec71a7872d 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -100,7 +100,8 @@ static int dnssec_rsa_verify_raw( return -EIO; e = m = NULL; - assert((size_t) RSA_size(rpubkey) == signature_size); + if ((size_t) RSA_size(rpubkey) != signature_size) + return -EINVAL; epubkey = EVP_PKEY_new(); if (!epubkey) @@ -230,9 +231,11 @@ static int dnssec_ecdsa_verify_raw( if (EC_KEY_set_public_key(eckey, p) <= 0) return log_debug_errno(SYNTHETIC_ERRNO(EIO), - "EC_POINT_bn2point failed: 0x%lx", ERR_get_error()); + "EC_KEY_set_public_key failed: 0x%lx", ERR_get_error()); - assert(EC_KEY_check_key(eckey) == 1); + if (EC_KEY_check_key(eckey) != 1) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "EC_KEY_check_key failed: 0x%lx", ERR_get_error()); r = BN_bin2bn(signature_r, signature_r_size, NULL); if (!r) From aacb3460f83bb2abb2e501e645015f6555789219 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Fri, 10 Apr 2026 10:52:21 +0200 Subject: [PATCH 06/29] sd-bus: don't overallocate the message buffer newa(t, n) already allocates sizeof(t) * n bytes, so previously we'd actually allocate sizeof(t) * sizeof(t) * n bytes, which is ~16x more (on x86_64) that we actually needed. This is probably an oversight from a tree-wide change in 6e9417f5b4f29938fab1eee2b5edf596cc580452 that replaced alloca() with newa(). Follow-up for 6e9417f5b4f29938fab1eee2b5edf596cc580452. (cherry picked from commit 92d87ac3029a5fc6b7c94a4196e0d2b4db2cef3a) --- src/libsystemd/sd-bus/bus-socket.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index 4cac317dffca6..57a540e90e399 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -1235,7 +1235,6 @@ int bus_socket_take_fd(sd_bus *b) { int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) { struct iovec *iov; ssize_t k; - size_t n; unsigned j; int r; @@ -1251,9 +1250,8 @@ int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) { if (r < 0) return r; - n = m->n_iovec * sizeof(struct iovec); - iov = newa(struct iovec, n); - memcpy_safe(iov, m->iovec, n); + iov = newa(struct iovec, m->n_iovec); + memcpy_safe(iov, m->iovec, sizeof(struct iovec) * m->n_iovec); j = 0; iovec_advance(iov, &j, *idx); From 708944017fd59169ec03c8084f7d8a422fe61744 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 10 Apr 2026 08:14:53 +0200 Subject: [PATCH 07/29] meson: Check if files returned by git ls-files actually exist Otherwise you run into errors such as: """ ../meson.build:2899:28: ERROR: File src/test/test-loop-util.c does not exist. """ when deleting a file in git without staging the deletion. (cherry picked from commit 8355eb6e1109b91654363fae01b903735895c295) --- meson.build | 8 +++++++- test/fuzz/meson.build | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 3672005d75b17..646c0ed5217c5 100644 --- a/meson.build +++ b/meson.build @@ -2866,7 +2866,13 @@ if git.found() 'ls-files', ':/*.[ch]', ':/*.cc', check : false) if all_files.returncode() == 0 - all_files = files(all_files.stdout().split()) + existing_files = [] + foreach f : all_files.stdout().split() + if fs.exists(f) + existing_files += f + endif + endforeach + all_files = files(existing_files) custom_target( output : 'tags', diff --git a/test/fuzz/meson.build b/test/fuzz/meson.build index 6f9f43a4105f9..d19fda3a02eaa 100644 --- a/test/fuzz/meson.build +++ b/test/fuzz/meson.build @@ -51,7 +51,7 @@ foreach p : out.stdout().split() # # Also, backslashes get mangled, so skip test. See # https://github.com/mesonbuild/meson/issues/1564. - if p.contains('\\') + if p.contains('\\') or not fs.exists(p) continue endif fuzzer = fs.name(fs.parent(p)) From 308a8f19ca7d083b1382faf358351ddb457039d3 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Fri, 10 Apr 2026 17:20:03 +0200 Subject: [PATCH 08/29] sd-json: limit the stack depth during parsing as well (cherry picked from commit 1016dd315f94917cd0818a90bc09c99ef76ab556) --- src/libsystemd/sd-json/sd-json.c | 12 +++++++++ src/test/test-json.c | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/libsystemd/sd-json/sd-json.c b/src/libsystemd/sd-json/sd-json.c index 7829e11880643..ae0d60357c5b1 100644 --- a/src/libsystemd/sd-json/sd-json.c +++ b/src/libsystemd/sd-json/sd-json.c @@ -3117,6 +3117,12 @@ static int json_parse_internal( goto finish; } + /* n_stack includes the top level entry, hence > instead of >= */ + if (n_stack > DEPTH_MAX) { + r = -ELNRNG; + goto finish; + } + if (!GREEDY_REALLOC(stack, n_stack+1)) { r = -ENOMEM; goto finish; @@ -3168,6 +3174,12 @@ static int json_parse_internal( goto finish; } + /* n_stack includes the top level entry, hence > instead of >= */ + if (n_stack > DEPTH_MAX) { + r = -ELNRNG; + goto finish; + } + if (!GREEDY_REALLOC(stack, n_stack+1)) { r = -ENOMEM; goto finish; diff --git a/src/test/test-json.c b/src/test/test-json.c index 679152fd955a8..f10bf9c7cad68 100644 --- a/src/test/test-json.c +++ b/src/test/test-json.c @@ -555,6 +555,49 @@ TEST(depth) { fputs("\n", stdout); } +static char *prepare_nested_json(const char *open, unsigned depth) { + char *s, *p; + size_t olen; + + assert_se(open); + + olen = strlen(open); + s = p = new(char, olen * depth + 1); + if (!s) + return NULL; + + for (unsigned i = 0; i < depth; i++) + p = mempcpy(p, open, olen); + *p = '\0'; + + return s; +} + +TEST(parse_depth) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + _cleanup_free_ char *s = NULL; + + /* Refuse parsing > DEPTH_MAX (currently 2048) levels of nested arrays */ + s = prepare_nested_json("[", 2049); + ASSERT_ERROR(sd_json_parse(s, 0, &v, NULL, NULL), ELNRNG); + s = mfree(s); + + /* Same for nested objects */ + s = prepare_nested_json("{\"a\":", 2049); + ASSERT_ERROR(sd_json_parse(s, 0, &v, NULL, NULL), ELNRNG); + s = mfree(s); + + /* <= DEPTH_MAX levels of nested arrays should be refused by EINVAL + * later in the parsing process */ + s = prepare_nested_json("[", 2048); + ASSERT_ERROR(sd_json_parse(s, 0, &v, NULL, NULL), EINVAL); + s = mfree(s); + + /* And the same for nested objects */ + s = prepare_nested_json("{\"a\":", 2048); + ASSERT_ERROR(sd_json_parse(s, 0, &v, NULL, NULL), EINVAL); +} + TEST(normalize) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *w = NULL; _cleanup_free_ char *t = NULL; From c99cecad702a11d9e5717546d54fd0bf1047c3a8 Mon Sep 17 00:00:00 2001 From: Nandakumar Raghavan Date: Tue, 24 Mar 2026 13:42:42 +0000 Subject: [PATCH 09/29] core: fix EBUSY on restart and clean of delegated services When a service is configured with Delegate=yes and DelegateSubgroup=sub, the delegated container may write domain controllers (e.g. "pids") into the service cgroup's cgroup.subtree_control via its cgroupns root. On container exit the stale controllers remain, and on service restart clone3() with CLONE_INTO_CGROUP fails with EBUSY because placing a process into a cgroup that has domain controllers in subtree_control violates the no-internal- processes rule. The same issue affects systemctl clean, where cg_attach() fails with EBUSY for the same reason. Add unit_cgroup_disable_all_controllers() helper in cgroup.c that clears stale controllers via cg_enable(mask=0) and updates cgroup_enabled_mask to keep internal tracking in sync. Call it from service_start() and service_clean() right before spawning, so that resource control is preserved for any lingering processes from the previous invocation as long as possible. (cherry picked from commit 056bc106e1e344f98cdfa86fdf62e6fed72958c9) --- src/core/cgroup.c | 23 +++++++++++++++++++++++ src/core/cgroup.h | 2 ++ src/core/service.c | 6 +++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 514dabf371b7f..79ad7f3cf29aa 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -3982,6 +3982,29 @@ bool unit_cgroup_delegate(Unit *u) { return c->delegate; } +void unit_cgroup_disable_all_controllers(Unit *u) { + int r; + + assert(u); + + CGroupRuntime *crt = unit_get_cgroup_runtime(u); + if (!crt || !crt->cgroup_path) + return; + + if (!unit_cgroup_delegate(u)) + return; + + /* For delegated units, the previous payload may have enabled controllers (e.g. "pids") in + * cgroup.subtree_control. These persist after the service stops and turn the cgroup into an + * "internal node", causing clone3(CLONE_INTO_CGROUP) to fail with EBUSY. Clear them now, right + * before the new start, so that resource control is preserved for lingering processes as long as + * possible. Ignore errors — if sub-cgroups still have live processes the write will fail, but so + * will the upcoming spawn. */ + r = cg_enable(u->manager->cgroup_supported, /* mask= */ 0, crt->cgroup_path, &crt->cgroup_enabled_mask); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to disable controllers on cgroup %s, ignoring: %m", empty_to_root(crt->cgroup_path)); +} + void manager_invalidate_startup_units(Manager *m) { Unit *u; diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 0cd290e92f25d..438ef8cc50875 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -442,6 +442,8 @@ void unit_cgroup_catchup(Unit *u); bool unit_cgroup_delegate(Unit *u); +void unit_cgroup_disable_all_controllers(Unit *u); + int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name); int unit_cgroup_freezer_action(Unit *u, FreezerAction action); diff --git a/src/core/service.c b/src/core/service.c index 51bba291e8fd4..dbb08f4bdb6b3 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -13,6 +13,7 @@ #include "bus-common-errors.h" #include "bus-error.h" #include "bus-util.h" +#include "cgroup.h" #include "chase.h" #include "dbus-service.h" #include "dbus-unit.h" @@ -3165,8 +3166,10 @@ static int service_start(Unit *u) { exec_status_reset(&s->main_exec_status); CGroupRuntime *crt = unit_get_cgroup_runtime(u); - if (crt) + if (crt) { + unit_cgroup_disable_all_controllers(u); crt->reset_accounting = true; + } service_enter_condition(s); return 1; @@ -5629,6 +5632,7 @@ static int service_clean(Unit *u, ExecCleanMask mask) { goto fail; } + unit_cgroup_disable_all_controllers(u); r = unit_fork_and_watch_rm_rf(u, l, &s->control_pid); if (r < 0) { log_unit_warning_errno(u, r, "Failed to spawn cleaning task: %m"); From 6f0c9b72b5f0b4d2b19bfbfe5a319686fdfcd7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 10 Apr 2026 18:26:58 +0200 Subject: [PATCH 10/29] importctl: fix -N to actually clear keep-download flag -N was clearing and re-setting the same bit in arg_import_flags_mask, which is a no-op. It should clear the bit in arg_import_flags instead, matching what --keep-download=no does via SET_FLAG(). (cherry picked from commit ee96f934c6efccd4a2a3fe1073f4da961fe4eb25) --- src/import/importctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/import/importctl.c b/src/import/importctl.c index c2ddd08f27624..c7554aa517456 100644 --- a/src/import/importctl.c +++ b/src/import/importctl.c @@ -1269,7 +1269,7 @@ static int parse_argv(int argc, char *argv[]) { break; case 'N': - arg_import_flags_mask &= ~IMPORT_PULL_KEEP_DOWNLOAD; + arg_import_flags &= ~IMPORT_PULL_KEEP_DOWNLOAD; arg_import_flags_mask |= IMPORT_PULL_KEEP_DOWNLOAD; break; From c96d5d2104e6abd4080c0d8e372b57992f9db273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 11 Apr 2026 12:33:44 +0200 Subject: [PATCH 11/29] sd-event: replace dead code path with an assert Coverity complains that the -EOPNOTSUPP can never be returned, because we always have !watch_fallback==locked. CID#1654169 (cherry picked from commit 1c9fbbab1deb81e520b94419ce03837436643c58) --- src/libsystemd/sd-event/sd-event.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index b78cfe86fa40e..ca3f97ba1ffdb 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -2025,11 +2025,13 @@ _public_ int sd_event_add_memory_pressure( if (errno != ENOENT) return -errno; - /* We got ENOENT. Three options now: try the fallback if we have one, or return the error as - * is (if based on user/env config), or return -EOPNOTSUPP (because we picked the path, and - * the PSI service apparently is not supported) */ - if (!watch_fallback) - return locked ? -ENOENT : -EOPNOTSUPP; + /* We got ENOENT. Two options now: try the fallback if we have one, or return the error as is + * (when based on user/env config). */ + + if (!watch_fallback) { + assert(locked); + return -ENOENT; + } path_fd = open(watch_fallback, O_PATH|O_CLOEXEC); if (path_fd < 0) { From 379994d9eb9d0991b82aeae6e18c2ece551fda7f Mon Sep 17 00:00:00 2001 From: joo es Date: Sat, 11 Apr 2026 19:58:51 +0000 Subject: [PATCH 12/29] po: Translated using Weblate (Arabic) Currently translated at 100.0% (266 of 266 strings) Co-authored-by: joo es Translate-URL: https://translate.fedoraproject.org/projects/systemd/main/ar/ Translation: systemd/main (cherry picked from commit 043689f8e098c3fd5f522b4d1d2b74d4fd9ee252) --- po/ar.po | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/po/ar.po b/po/ar.po index e4a8845ebfa35..1c9882eaa322d 100644 --- a/po/ar.po +++ b/po/ar.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-06 03:46+0900\n" -"PO-Revision-Date: 2026-02-26 13:58+0000\n" +"PO-Revision-Date: 2026-04-11 19:58+0000\n" "Last-Translator: joo es \n" "Language-Team: Arabic \n" @@ -18,7 +18,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" -"X-Generator: Weblate 5.16.1\n" +"X-Generator: Weblate 5.16.2\n" #: src/core/org.freedesktop.systemd1.policy.in:22 msgid "Send passphrase back to system" @@ -938,12 +938,10 @@ msgid "DHCP server sends force renew message" msgstr "خادم DHCP يرسل رسالة تجديد إجبارية" #: src/network/org.freedesktop.network1.policy:144 -#, fuzzy -#| msgid "Authentication is required to send force renew message." msgid "" "Authentication is required to send a force renew message from the DHCP " "server." -msgstr "الاستيثاق مطلوب للإرسال رسالة تجديد إجبارية." +msgstr "الاستيثاق مطلوب للإرسال رسالة تجديد إجبارية من خادم DHCP." #: src/network/org.freedesktop.network1.policy:154 msgid "Renew dynamic addresses" From 764ec4ce618c09e8b3e6c85956beffaae9f4e5ce Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Fri, 10 Apr 2026 18:32:33 +0200 Subject: [PATCH 13/29] sd-varlink: scale down the limit of connections per UID to 128 1024 connections per UID is unnecessarily generous, so let's scale this down a bit. D-Bus defaults to 256 connections per UID, but let's be even more conservative and go with 128. (cherry picked from commit d9da339bf12f6433eaeb624589956f2f8737a6a0) --- src/libsystemd/sd-varlink/sd-varlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsystemd/sd-varlink/sd-varlink.c b/src/libsystemd/sd-varlink/sd-varlink.c index 8e8fc03a030e5..c865eee1eea38 100644 --- a/src/libsystemd/sd-varlink/sd-varlink.c +++ b/src/libsystemd/sd-varlink/sd-varlink.c @@ -41,7 +41,7 @@ #include "varlink-org.varlink.service.h" #define VARLINK_DEFAULT_CONNECTIONS_MAX 4096U -#define VARLINK_DEFAULT_CONNECTIONS_PER_UID_MAX 1024U +#define VARLINK_DEFAULT_CONNECTIONS_PER_UID_MAX 128U #define VARLINK_DEFAULT_TIMEOUT_USEC (45U*USEC_PER_SEC) #define VARLINK_BUFFER_MAX (16U*1024U*1024U) From e4be9063dab27ae4ecc4529e50d7c92453430b3d Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sat, 11 Apr 2026 22:04:37 +0100 Subject: [PATCH 14/29] uid-range: add assert to prevent underflow in coalesce loop Coverity flags range->n_entries - j as a potential underflow in the memmove size calculation. Add assert(range->n_entries > 0) before decrementing n_entries, which holds since the loop condition guarantees j < n_entries. CID#1548015 Follow-up for 8dcc66cefc8ab489568c737adcba960756d76a3c (cherry picked from commit ff102359b7cf767c9c793a163ff34d95370d0107) --- src/basic/uid-range.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/basic/uid-range.c b/src/basic/uid-range.c index 31305952ba43c..be04d557d45b3 100644 --- a/src/basic/uid-range.c +++ b/src/basic/uid-range.c @@ -71,6 +71,8 @@ static void uid_range_coalesce(UIDRange *range) { if (range->n_entries > j + 1) memmove(y, y + 1, sizeof(UIDRangeEntry) * (range->n_entries - j - 1)); + /* Silence static analyzers, n_entries > 0 since j < n_entries holds in the loop condition */ + assert(range->n_entries > 0); range->n_entries--; j--; } From 9bc20be402e45746849028424c3fd4dea2ffd409 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sat, 11 Apr 2026 22:50:39 +0100 Subject: [PATCH 15/29] debug-generator: assert breakpoint type is valid before bit shift The BreakpointType enum includes _BREAKPOINT_TYPE_INVALID (-EINVAL), so Coverity flags the bit shift as potentially using a negative shift amount. Add an assert to verify the type is in valid range, since the static table only contains valid entries. CID#1568482 Follow-up for 1929226e7e649b72f3f9acd464eaac771c00945c (cherry picked from commit efccc0dc2311ef21cb10334a2594616f340a415f) --- src/debug-generator/debug-generator.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c index 878e1152328ae..7b6b2ba0acbe9 100644 --- a/src/debug-generator/debug-generator.c +++ b/src/debug-generator/debug-generator.c @@ -101,7 +101,8 @@ static int parse_breakpoint_from_string(const char *s, uint32_t *ret_breakpoints FOREACH_ELEMENT(i, breakpoint_info_table) if (FLAGS_SET(i->validity, BREAKPOINT_DEFAULT) && breakpoint_applies(i, INT_MAX)) { - breakpoints |= 1 << i->type; + assert(i->type >= 0 && i->type < _BREAKPOINT_TYPE_MAX); /* silence coverity */ + breakpoints |= UINT32_C(1) << i->type; found_default = true; break; } From 4e950cdfee80b8b446457f949fb33d8d9968ada5 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Sun, 12 Apr 2026 15:43:35 +0200 Subject: [PATCH 16/29] journal: move the {DATA,ENTRY}_SIZE constants to sd-journal So we can access them from the code there as well. (cherry picked from commit d830bb1fc132d31b5e82ba3c676051a4000d1538) --- src/core/unit.h | 2 +- src/coredump/coredump-config.c | 2 +- src/journal/journald-native.c | 1 - src/libsystemd/sd-journal/journal-def.h | 15 +++++++++++++++ src/shared/journal-importer.h | 14 -------------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/core/unit.h b/src/core/unit.h index 380ce7fac8c08..e503c2e344af5 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -10,7 +10,7 @@ #include "install.h" #include "iterator.h" #include "job.h" -#include "journal-importer.h" +#include "journal-def.h" #include "list.h" #include "log.h" #include "log-context.h" diff --git a/src/coredump/coredump-config.c b/src/coredump/coredump-config.c index de2bb68bf3151..cb7bb1adcc485 100644 --- a/src/coredump/coredump-config.c +++ b/src/coredump/coredump-config.c @@ -3,7 +3,7 @@ #include "conf-parser.h" #include "coredump-config.h" #include "format-util.h" -#include "journal-importer.h" +#include "journal-def.h" #include "log.h" #include "string-table.h" #include "string-util.h" diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index d61e8f5a743f7..0972becad07cc 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -10,7 +10,6 @@ #include "fd-util.h" #include "format-util.h" #include "iovec-util.h" -#include "journal-importer.h" #include "journal-internal.h" #include "journald-client.h" #include "journald-console.h" diff --git a/src/libsystemd/sd-journal/journal-def.h b/src/libsystemd/sd-journal/journal-def.h index f20c9a357a195..84849a1e9506d 100644 --- a/src/libsystemd/sd-journal/journal-def.h +++ b/src/libsystemd/sd-journal/journal-def.h @@ -6,6 +6,21 @@ #include "sd-forward.h" #include "sparse-endian.h" +/* Make sure not to make this smaller than the maximum coredump size. + * See JOURNAL_SIZE_MAX in coredump-config.h */ +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define ENTRY_SIZE_MAX (1024*1024*770u) +#define ENTRY_SIZE_UNPRIV_MAX (1024*1024*32u) +#define DATA_SIZE_MAX (1024*1024*768u) +#else +#define ENTRY_SIZE_MAX (1024*1024*13u) +#define ENTRY_SIZE_UNPRIV_MAX (1024*1024*8u) +#define DATA_SIZE_MAX (1024*1024*11u) +#endif + +/* The maximum number of fields in an entry */ +#define ENTRY_FIELD_COUNT_MAX 1024u + /* * If you change this file you probably should also change its documentation: * diff --git a/src/shared/journal-importer.h b/src/shared/journal-importer.h index f218d80dfd938..21de703781b89 100644 --- a/src/shared/journal-importer.h +++ b/src/shared/journal-importer.h @@ -8,22 +8,8 @@ #include "iovec-wrapper.h" #include "time-util.h" -/* Make sure not to make this smaller than the maximum coredump size. - * See JOURNAL_SIZE_MAX in coredump.c */ -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#define ENTRY_SIZE_MAX (1024*1024*770u) -#define ENTRY_SIZE_UNPRIV_MAX (1024*1024*32u) -#define DATA_SIZE_MAX (1024*1024*768u) -#else -#define ENTRY_SIZE_MAX (1024*1024*13u) -#define ENTRY_SIZE_UNPRIV_MAX (1024*1024*8u) -#define DATA_SIZE_MAX (1024*1024*11u) -#endif #define LINE_CHUNK 8*1024u -/* The maximum number of fields in an entry */ -#define ENTRY_FIELD_COUNT_MAX 1024u - typedef struct JournalImporter { int fd; bool passive_fd; From 79f42ffba379967a8d0dc4f9cc7a79e5b20db5a7 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Sun, 12 Apr 2026 16:24:53 +0200 Subject: [PATCH 17/29] compress: limit the output to dst_max bytes with LZ4 if set We already do that with other algorithms, so let's make decompress_blob_lz4() consistent with the rest. (cherry picked from commit 2cda5f6169e4a03e9860d315e7b4a7b0d61ca11f) --- src/basic/compress.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/basic/compress.c b/src/basic/compress.c index c10448938e071..e48b67146bfdb 100644 --- a/src/basic/compress.c +++ b/src/basic/compress.c @@ -434,6 +434,8 @@ int decompress_blob_lz4( size = unaligned_read_le64(src); if (size < 0 || (unsigned) size != unaligned_read_le64(src)) return -EFBIG; + if (dst_max > 0 && (size_t) size > dst_max) + return -ENOBUFS; out = greedy_realloc(dst, size, 1); if (!out) return -ENOMEM; From e043d168857f00cd420d7f9c6b7f39b3d6cc364f Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Sun, 12 Apr 2026 15:02:11 +0200 Subject: [PATCH 18/29] journal: limit decompress_blob() output to DATA_SIZE_MAX We already have checks in place during compression that limit the data we compress, so they shouldn't decompress to anything larger than DATA_SIZE_MAX unless they've been tampered with. Let's make this explicit and limit all our decompress_blob() calls in journal-handling code to that limit. One possible scenario this should prevent is when one tries to open and verify a journal file that contains a compression bomb in its payload: $ ls -lh test.journal -rw-rw-r--+ 1 fsumsal fsumsal 1.2M Apr 12 15:07 test.journal $ systemd-run --user --wait --pipe -- build-local/journalctl --verify --file=$PWD/test.journal Running as unit: run-p682422-i4875779.service 000110: Invalid hash (00000000 vs. 11e4948d73bdafdd) 000110: Invalid object contents: Bad message File corruption detected at /home/fsumsal/repos/@systemd/systemd/test.journal:272 (of 1249896 bytes, 0%). FAIL: /home/fsumsal/repos/@systemd/systemd/test.journal (Bad message) Finished with result: exit-code Main processes terminated with: code=exited, status=1/FAILURE Service runtime: 48.051s CPU time consumed: 47.941s Memory peak: 8G (swap: 0B) Same could be, in theory, possible with just `journalctl --file=`, but the reproducer would be a bit more complicated (haven't tried it, yet). Lastly, the change in journal-remote is mostly hardening, as the maximum input size to decompress_blob() there is mandated by MHD's connection memory limit (set to JOURNAL_SERVER_MEMORY_MAX which is 128 KiB at the time of writing), so the possible output size there is already quite limited (e.g. ~800 - 900 MiB for xz-compressed data). (cherry picked from commit 31d360fb0b28859aba891aaefb1452f820a5861a) --- src/journal-remote/journal-remote-main.c | 2 +- src/libsystemd/sd-journal/journal-file.c | 2 +- src/libsystemd/sd-journal/journal-verify.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c index 35ab12578b2a0..704b79b49ccac 100644 --- a/src/journal-remote/journal-remote-main.c +++ b/src/journal-remote/journal-remote-main.c @@ -286,7 +286,7 @@ static int process_http_upload( _cleanup_free_ char *buf = NULL; size_t buf_size; - r = decompress_blob(source->compression, upload_data, *upload_data_size, (void **) &buf, &buf_size, 0); + r = decompress_blob(source->compression, upload_data, *upload_data_size, (void **) &buf, &buf_size, DATA_SIZE_MAX); if (r < 0) return mhd_respondf(connection, r, MHD_HTTP_BAD_REQUEST, "Decompression of received blob failed."); diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index de5d7075e0cca..31e8e27c8714a 100644 --- a/src/libsystemd/sd-journal/journal-file.c +++ b/src/libsystemd/sd-journal/journal-file.c @@ -1967,7 +1967,7 @@ static int maybe_decompress_payload( } } - r = decompress_blob(compression, payload, size, &f->compress_buffer, &rsize, 0); + r = decompress_blob(compression, payload, size, &f->compress_buffer, &rsize, DATA_SIZE_MAX); if (r < 0) return r; diff --git a/src/libsystemd/sd-journal/journal-verify.c b/src/libsystemd/sd-journal/journal-verify.c index 0afb664896aca..1306e82878c28 100644 --- a/src/libsystemd/sd-journal/journal-verify.c +++ b/src/libsystemd/sd-journal/journal-verify.c @@ -124,7 +124,7 @@ static int hash_payload(JournalFile *f, Object *o, uint64_t offset, const uint8_ _cleanup_free_ void *b = NULL; size_t b_size; - r = decompress_blob(c, src, size, &b, &b_size, 0); + r = decompress_blob(c, src, size, &b, &b_size, DATA_SIZE_MAX); if (r < 0) { error_errno(offset, r, "%s decompression failed: %m", compression_to_string(c)); From 50099a9f60225fae25c781b45e63580676232719 Mon Sep 17 00:00:00 2001 From: Milan Kyselica Date: Sat, 11 Apr 2026 10:31:16 +0200 Subject: [PATCH 19/29] nss-systemd: fix off-by-one in nss_pack_group_record_shadow() nss_count_strv() counts trailing NULL pointers in n. The pointer area then used (n + 1), reserving one slot more than the size check accounted for. Drop the + 1 since n already includes the trailing NULLs, unlike the non-shadow nss_pack_group_record() where n does not. Fixes: https://github.com/systemd/systemd/issues/41591 (cherry picked from commit aa85a742fe5e0816312566a700599496e720246d) --- src/nss-systemd/userdb-glue.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nss-systemd/userdb-glue.c b/src/nss-systemd/userdb-glue.c index 1d5e311ce8653..be99df00bf2cf 100644 --- a/src/nss-systemd/userdb-glue.c +++ b/src/nss-systemd/userdb-glue.c @@ -472,7 +472,9 @@ int nss_pack_group_record_shadow( assert(buffer); - p = buffer + sizeof(void*) * (n + 1); /* place member strings right after the ptr array */ + /* n already includes trailing NULL pointers from nss_count_strv(), unlike the + * non-shadow nss_pack_group_record() where n does not include them. */ + p = buffer + sizeof(void*) * n; array = (char**) buffer; /* place ptr array at beginning of buffer, under assumption buffer is aligned */ sgrp->sg_mem = array; From 95263463f46932980d567fd0cc8a27e96709f9e5 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Fri, 10 Apr 2026 19:04:04 +0100 Subject: [PATCH 20/29] udev/scsi-id: check for invalid chars in various fields received from the kernel Follow-up for 16325b35fa6ecb25f66534a562583ce3b96d52f3 (cherry picked from commit 5f700d148c44063c0f0dbb9fc136866339cd3fa7) --- src/udev/scsi_id/scsi_id.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c index e3438897199f9..6a31f9c4c8ebd 100644 --- a/src/udev/scsi_id/scsi_id.c +++ b/src/udev/scsi_id/scsi_id.c @@ -389,6 +389,10 @@ static int set_inq_values(struct scsi_id_device *dev_scsi, const char *path) { return 0; } +static bool scsi_string_is_valid(const char *s) { + return !isempty(s) && utf8_is_valid(s) && !string_has_cc(s, /* ok= */ NULL); +} + /* * scsi_id: try to get an id, if one is found, printf it to stdout. * returns a value passed to exit() - 0 if printed an id, else 1. @@ -432,17 +436,17 @@ static int scsi_id(char *maj_min_dev) { udev_replace_chars(serial_str, NULL); printf("ID_SERIAL_SHORT=%s\n", serial_str); } - if (dev_scsi.wwn[0] != '\0') { + if (scsi_string_is_valid(dev_scsi.wwn)) { printf("ID_WWN=0x%s\n", dev_scsi.wwn); - if (dev_scsi.wwn_vendor_extension[0] != '\0') { + if (scsi_string_is_valid(dev_scsi.wwn_vendor_extension)) { printf("ID_WWN_VENDOR_EXTENSION=0x%s\n", dev_scsi.wwn_vendor_extension); printf("ID_WWN_WITH_EXTENSION=0x%s%s\n", dev_scsi.wwn, dev_scsi.wwn_vendor_extension); } else printf("ID_WWN_WITH_EXTENSION=0x%s\n", dev_scsi.wwn); } - if (dev_scsi.tgpt_group[0] != '\0') + if (scsi_string_is_valid(dev_scsi.tgpt_group)) printf("ID_TARGET_PORT=%s\n", dev_scsi.tgpt_group); - if (dev_scsi.unit_serial_number[0] != '\0' && utf8_is_valid(dev_scsi.unit_serial_number) && !string_has_cc(dev_scsi.unit_serial_number, /* ok= */ NULL)) + if (scsi_string_is_valid(dev_scsi.unit_serial_number)) printf("ID_SCSI_SERIAL=%s\n", dev_scsi.unit_serial_number); goto out; } From 104c78085f1791750f8146ddd0db59b3c193c561 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Fri, 10 Apr 2026 19:35:59 +0100 Subject: [PATCH 21/29] udev/scsi-id: check for invalid header from kernel buffer (cherry picked from commit 06d3f37336ab8dea545521d95ebc6246b29241f0) --- src/udev/scsi_id/scsi_serial.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c index 20caf695bf47a..40589fd66b267 100644 --- a/src/udev/scsi_id/scsi_serial.c +++ b/src/udev/scsi_id/scsi_serial.c @@ -536,7 +536,9 @@ static int check_fill_0x83_prespc3(struct scsi_id_device *dev_scsi, /* serial has been memset to zero before */ j = strlen(serial); /* j = 1; */ - for (i = 0; (i < page_83[3]) && (j < max_len-3); ++i) { + /* Cap reported page length to buffer size in case of malformed responses */ + int page_len = MIN((int)page_83[3], SCSI_INQ_BUFF_LEN - 4); + for (i = 0; (i < page_len) && (j < max_len-3); ++i) { serial[j++] = hexchar(page_83[4+i] >> 4); serial[j++] = hexchar(page_83[4+i]); } @@ -604,12 +606,25 @@ static int do_scsi_page83_inquiry(struct scsi_id_device *dev_scsi, int fd, * Search for a match in the prioritized id_search_list - since WWN ids * come first we can pick up the WWN in check_fill_0x83_id(). */ + + /* Cap reported page length to buffer size in case of malformed responses. + * Below, j can equal page_end, and at that point page_83[j + 3] (the first descriptor data byte) + * must still be readable before the inner bounds check, so page_end + 4 < SCSI_INQ_BUFF_LEN + * requires page_end <= SCSI_INQ_BUFF_LEN - 5. */ + unsigned page_end = MIN(((unsigned)page_83[2] << 8) + (unsigned)page_83[3] + 3U, + (unsigned)SCSI_INQ_BUFF_LEN - 5U); + FOREACH_ELEMENT(search_value, id_search_list) { /* * Examine each descriptor returned. There is normally only * one or a small number of descriptors. */ - for (unsigned j = 4; j <= ((unsigned)page_83[2] << 8) + (unsigned)page_83[3] + 3; j += page_83[j + 3] + 4) { + for (unsigned j = 4; j <= page_end; j += page_83[j + 3] + 4) { + /* Ensure the full descriptor fits within the buffer, including + * fixed-offset accesses up to page_83[7] in the TGTGROUP path + * of check_fill_0x83_id(), so require at least 8 bytes from j */ + if (j + MAX(4U + (unsigned)page_83[j + 3], 8U) > (unsigned)SCSI_INQ_BUFF_LEN) + break; retval = check_fill_0x83_id(dev_scsi, page_83 + j, search_value, serial, serial_short, len, @@ -682,7 +697,9 @@ static int do_scsi_page83_prespc3_inquiry(struct scsi_id_device *dev_scsi, int f * using two bytes of ASCII for each byte * in the page_83. */ - while (i < (page_83[3]+4)) { + /* Cap reported page length to buffer size in case of malformed responses */ + int page_len = MIN((int)page_83[3] + 4, SCSI_INQ_BUFF_LEN); + while (i < page_len && j + 2 < len) { serial[j++] = hexchar(page_83[i] >> 4); serial[j++] = hexchar(page_83[i]); i++; @@ -719,7 +736,8 @@ static int do_scsi_page80_inquiry(struct scsi_id_device *dev_scsi, int fd, * Prepend 'S' to avoid unlikely collision with page 0x83 vendor * specific type where we prepend '0' + vendor + model. */ - len = buf[3]; + /* Cap reported page length to buffer size in case of malformed responses */ + len = MIN((int)buf[3], SCSI_INQ_BUFF_LEN - 4); if (serial) { serial[0] = 'S'; ser_ind = append_vendor_model(dev_scsi, serial + 1); @@ -854,7 +872,10 @@ int scsi_get_serial(struct scsi_id_device *dev_scsi, const char *devname, goto completed; } - for (ind = 4; ind <= page0[3] + 3; ind++) + /* Cap reported page length to buffer size in case of malformed responses */ + int page0_end = MIN((int)page0[3] + 3, SCSI_INQ_BUFF_LEN - 1); + + for (ind = 4; ind <= page0_end; ind++) if (page0[ind] == PAGE_83) if (!do_scsi_page83_inquiry(dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { @@ -865,7 +886,7 @@ int scsi_get_serial(struct scsi_id_device *dev_scsi, const char *devname, goto completed; } - for (ind = 4; ind <= page0[3] + 3; ind++) + for (ind = 4; ind <= page0_end; ind++) if (page0[ind] == PAGE_80) if (!do_scsi_page80_inquiry(dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len)) { From 841fd490cae3211dedf3e620094333eb6cf7b4c8 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Fri, 10 Apr 2026 22:14:45 +0100 Subject: [PATCH 22/29] udev/scsi-id: various typing refactorings (cherry picked from commit 7fbdc6a8eba231afc5ac3e747c1281991e6ae90a) --- src/udev/scsi_id/scsi.h | 2 +- src/udev/scsi_id/scsi_id.h | 4 +- src/udev/scsi_id/scsi_serial.c | 73 +++++++++++++++++----------------- 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/udev/scsi_id/scsi.h b/src/udev/scsi_id/scsi.h index ebb8ae9008be9..71c532e427ac5 100644 --- a/src/udev/scsi_id/scsi.h +++ b/src/udev/scsi_id/scsi.h @@ -24,7 +24,7 @@ struct scsi_ioctl_command { * as this is a nice value for some devices, especially some of the usb * mass storage devices. */ -#define SCSI_INQ_BUFF_LEN 254 +#define SCSI_INQ_BUFF_LEN 254U /* * SCSI INQUIRY vendor and model (really product) lengths. diff --git a/src/udev/scsi_id/scsi_id.h b/src/udev/scsi_id/scsi_id.h index db49c7f3d955c..984bbc05f4ca6 100644 --- a/src/udev/scsi_id/scsi_id.h +++ b/src/udev/scsi_id/scsi_id.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once +#include "basic-forward.h" + /* * Copyright © IBM Corp. 2003 */ @@ -50,7 +52,7 @@ struct scsi_id_device { int scsi_std_inquiry(struct scsi_id_device *dev_scsi, const char *devname); int scsi_get_serial(struct scsi_id_device *dev_scsi, const char *devname, - int page_code, int len); + int page_code, size_t len); /* * Page code values. diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c index 40589fd66b267..b5f2ebea5ebf4 100644 --- a/src/udev/scsi_id/scsi_serial.c +++ b/src/udev/scsi_id/scsi_serial.c @@ -80,7 +80,7 @@ static const struct scsi_id_search_values id_search_list[] = { #define SG_ERR_CAT_OTHER 99 /* Some other error/warning */ static int do_scsi_page80_inquiry(struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int max_len); + char *serial, char *serial_short, size_t max_len); static int sg_err_category_new(int scsi_status, int msg_status, int host_status, int driver_status, const @@ -418,12 +418,12 @@ static int append_vendor_model( * serial number. */ static int check_fill_0x83_id(struct scsi_id_device *dev_scsi, - unsigned char *page_83, + uint8_t *page_83, const struct scsi_id_search_values *id_search, char *serial, char *serial_short, - int max_len, char *wwn, + size_t max_len, char *wwn, char *wwn_vendor_extension, char *tgpt_group) { - int i, j, s, len; + size_t i, j, s, len; /* * ASSOCIATION must be with the device (value 0) @@ -469,7 +469,7 @@ static int check_fill_0x83_id(struct scsi_id_device *dev_scsi, len += VENDOR_LENGTH + MODEL_LENGTH; if (max_len < len) { - log_debug("%s: length %d too short - need %d", + log_debug("%s: length %zu too short - need %zu", dev_scsi->kernel, max_len, len); return 1; } @@ -500,14 +500,14 @@ static int check_fill_0x83_id(struct scsi_id_device *dev_scsi, /* * ASCII descriptor. */ - while (i < (4 + page_83[3])) + while (i < (4U + page_83[3])) serial[j++] = page_83[i++]; } else { /* * Binary descriptor, convert to ASCII, using two bytes of * ASCII for each byte in the page_83. */ - while (i < (4 + page_83[3])) { + while (i < (4U + page_83[3])) { serial[j++] = hexchar(page_83[i] >> 4); serial[j++] = hexchar(page_83[i]); i++; @@ -527,18 +527,20 @@ static int check_fill_0x83_id(struct scsi_id_device *dev_scsi, /* Extract the raw binary from VPD 0x83 pre-SPC devices */ static int check_fill_0x83_prespc3(struct scsi_id_device *dev_scsi, - unsigned char *page_83, + uint8_t page_83[static SCSI_INQ_BUFF_LEN], const struct scsi_id_search_values - *id_search, char *serial, char *serial_short, int max_len) { - int i, j; + *id_search, char *serial, char *serial_short, size_t max_len) { + size_t j; + + assert(max_len > 0); serial[0] = hexchar(SCSI_ID_NAA); /* serial has been memset to zero before */ j = strlen(serial); /* j = 1; */ /* Cap reported page length to buffer size in case of malformed responses */ - int page_len = MIN((int)page_83[3], SCSI_INQ_BUFF_LEN - 4); - for (i = 0; (i < page_len) && (j < max_len-3); ++i) { + size_t page_len = MIN((size_t)page_83[3], SCSI_INQ_BUFF_LEN - 4); + for (size_t i = 0; (i < page_len) && (j + 3 < max_len); ++i) { serial[j++] = hexchar(page_83[4+i] >> 4); serial[j++] = hexchar(page_83[4+i]); } @@ -549,11 +551,11 @@ static int check_fill_0x83_prespc3(struct scsi_id_device *dev_scsi, /* Get device identification VPD page */ static int do_scsi_page83_inquiry(struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int len, + char *serial, char *serial_short, size_t len, char *unit_serial_number, char *wwn, char *wwn_vendor_extension, char *tgpt_group) { int retval; - unsigned char page_83[SCSI_INQ_BUFF_LEN]; + uint8_t page_83[SCSI_INQ_BUFF_LEN]; /* also pick up the page 80 serial number */ do_scsi_page80_inquiry(dev_scsi, fd, NULL, unit_serial_number, MAX_SERIAL_LEN); @@ -612,7 +614,7 @@ static int do_scsi_page83_inquiry(struct scsi_id_device *dev_scsi, int fd, * must still be readable before the inner bounds check, so page_end + 4 < SCSI_INQ_BUFF_LEN * requires page_end <= SCSI_INQ_BUFF_LEN - 5. */ unsigned page_end = MIN(((unsigned)page_83[2] << 8) + (unsigned)page_83[3] + 3U, - (unsigned)SCSI_INQ_BUFF_LEN - 5U); + SCSI_INQ_BUFF_LEN - 5U); FOREACH_ELEMENT(search_value, id_search_list) { /* @@ -623,7 +625,7 @@ static int do_scsi_page83_inquiry(struct scsi_id_device *dev_scsi, int fd, /* Ensure the full descriptor fits within the buffer, including * fixed-offset accesses up to page_83[7] in the TGTGROUP path * of check_fill_0x83_id(), so require at least 8 bytes from j */ - if (j + MAX(4U + (unsigned)page_83[j + 3], 8U) > (unsigned)SCSI_INQ_BUFF_LEN) + if (j + MAX(4U + (unsigned)page_83[j + 3], 8U) > SCSI_INQ_BUFF_LEN) break; retval = check_fill_0x83_id(dev_scsi, page_83 + j, search_value, @@ -647,10 +649,10 @@ static int do_scsi_page83_inquiry(struct scsi_id_device *dev_scsi, int fd, * conformant to the SCSI-2 format. */ static int do_scsi_page83_prespc3_inquiry(struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int len) { + char *serial, char *serial_short, size_t len) { int retval; - int i, j; - unsigned char page_83[SCSI_INQ_BUFF_LEN]; + size_t i, j; + uint8_t page_83[SCSI_INQ_BUFF_LEN]; memzero(page_83, SCSI_INQ_BUFF_LEN); retval = scsi_inquiry(dev_scsi, fd, 1, PAGE_83, page_83, SCSI_INQ_BUFF_LEN); @@ -698,7 +700,7 @@ static int do_scsi_page83_prespc3_inquiry(struct scsi_id_device *dev_scsi, int f * in the page_83. */ /* Cap reported page length to buffer size in case of malformed responses */ - int page_len = MIN((int)page_83[3] + 4, SCSI_INQ_BUFF_LEN); + size_t page_len = MIN((size_t)page_83[3] + 4, SCSI_INQ_BUFF_LEN); while (i < page_len && j + 2 < len) { serial[j++] = hexchar(page_83[i] >> 4); serial[j++] = hexchar(page_83[i]); @@ -709,12 +711,11 @@ static int do_scsi_page83_prespc3_inquiry(struct scsi_id_device *dev_scsi, int f /* Get unit serial number VPD page */ static int do_scsi_page80_inquiry(struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int max_len) { + char *serial, char *serial_short, size_t max_len) { int retval; int ser_ind; - int i; - int len; - unsigned char buf[SCSI_INQ_BUFF_LEN]; + size_t page_len; + uint8_t buf[SCSI_INQ_BUFF_LEN]; memzero(buf, SCSI_INQ_BUFF_LEN); retval = scsi_inquiry(dev_scsi, fd, 1, PAGE_80, buf, SCSI_INQ_BUFF_LEN); @@ -726,10 +727,10 @@ static int do_scsi_page80_inquiry(struct scsi_id_device *dev_scsi, int fd, return 1; } - len = 1 + VENDOR_LENGTH + MODEL_LENGTH + buf[3]; - if (max_len < len) { - log_debug("%s: length %d too short - need %d", - dev_scsi->kernel, max_len, len); + page_len = 1 + VENDOR_LENGTH + MODEL_LENGTH + buf[3]; + if (max_len < page_len) { + log_debug("%s: length %zu too short - need %zu", + dev_scsi->kernel, max_len, page_len); return 1; } /* @@ -737,19 +738,19 @@ static int do_scsi_page80_inquiry(struct scsi_id_device *dev_scsi, int fd, * specific type where we prepend '0' + vendor + model. */ /* Cap reported page length to buffer size in case of malformed responses */ - len = MIN((int)buf[3], SCSI_INQ_BUFF_LEN - 4); + page_len = MIN((size_t)buf[3], SCSI_INQ_BUFF_LEN - 4); if (serial) { serial[0] = 'S'; ser_ind = append_vendor_model(dev_scsi, serial + 1); if (ser_ind < 0) return 1; ser_ind++; /* for the leading 'S' */ - for (i = 4; i < len + 4; i++, ser_ind++) + for (size_t i = 4; i < page_len + 4; i++, ser_ind++) serial[ser_ind] = buf[i]; } if (serial_short) { - memcpy(serial_short, buf + 4, len); - serial_short[len] = '\0'; + memcpy(serial_short, buf + 4, page_len); + serial_short[page_len] = '\0'; } return 0; } @@ -793,11 +794,11 @@ int scsi_std_inquiry(struct scsi_id_device *dev_scsi, const char *devname) { } int scsi_get_serial(struct scsi_id_device *dev_scsi, const char *devname, - int page_code, int len) { - unsigned char page0[SCSI_INQ_BUFF_LEN]; + int page_code, size_t len) { + uint8_t page0[SCSI_INQ_BUFF_LEN]; int fd = -EBADF; int cnt; - int ind; + size_t ind; int retval; memzero(dev_scsi->serial, len); @@ -873,7 +874,7 @@ int scsi_get_serial(struct scsi_id_device *dev_scsi, const char *devname, } /* Cap reported page length to buffer size in case of malformed responses */ - int page0_end = MIN((int)page0[3] + 3, SCSI_INQ_BUFF_LEN - 1); + size_t page0_end = MIN((size_t)page0[3] + 3, SCSI_INQ_BUFF_LEN - 1); for (ind = 4; ind <= page0_end; ind++) if (page0[ind] == PAGE_83) From 403120b4348648222fd93068f9ba4876b371cf69 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 12 Apr 2026 21:44:59 +0200 Subject: [PATCH 23/29] time-util: encode our assumption that clock_gettime() never can return 0 or USEC_INFINITY We generally assume that valid times returned by clock_gettime() are > 0 and < USEC_INFINITY. If this wouldn't hold all kinds of things would break, because we couldn't distuingish our niche values from regular values anymore. Let's hence encode our assumptions in C, already to help static analyzers and LLMs. Inspired by: https://github.com/systemd/systemd/pull/41601#pullrequestreview-4094645891 (cherry picked from commit e700d5134df15a094a2c92bc61392fbaf3c0452b) --- src/basic/time-util.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 5dd00af952d29..57f24a9866ba2 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -49,7 +49,15 @@ usec_t now(clockid_t clock_id) { assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); - return timespec_load(&ts); + usec_t n = timespec_load(&ts); + + /* We use both 0 and USEC_INFINITY as niche values. If the current time collides with either, things are + * really weird and really broken. Let's not allow this to go through, it would break too many of our + * assumptions in code. */ + assert(n > 0); + assert(n < USEC_INFINITY); + + return n; } nsec_t now_nsec(clockid_t clock_id) { @@ -57,7 +65,12 @@ nsec_t now_nsec(clockid_t clock_id) { assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); - return timespec_load_nsec(&ts); + nsec_t n = timespec_load_nsec(&ts); + + assert(n > 0); + assert(n < NSEC_INFINITY); + + return n; } dual_timestamp* dual_timestamp_now(dual_timestamp *ts) { From dbac5bce3032772dfe08cadb7ce5f043c1695e67 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Fri, 10 Apr 2026 14:13:27 +0100 Subject: [PATCH 24/29] docs: beef up SECURITY.md rules for reporting With yeswehack.com suspended due to funding issues for triagers being worked out, reports on GH are starting to pile up. Explicitly define some ground rules to avoid noise and time wasting. (cherry picked from commit a1813a40ec77985d975b635653ae924c16afa1b6) --- docs/SECURITY.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/SECURITY.md b/docs/SECURITY.md index 0993f85da2bb6..6a3102a717416 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -18,3 +18,12 @@ Subscription to the Security Advisories and/or systemd-security mailing list is Those conditions should be backed by publicly accessible information (ideally, a track of posts and commits from the mail address in question). If you fall into one of those categories and wish to be subscribed, contact the maintainers or submit a **[subscription request](https://www.redhat.com/mailman/listinfo/systemd-security)**. + +# Requirements for a Valid Report + +- Please ensure the issue is reproducible on main. +- Please ensure a fully working, end-to-end reproducer is provided. +- Please ensure the reproducer is real-world and not simulated or abstracted. +- Please ensure the reproducer demonstrably violates a security boundary. +- Please understand that most of our maintainers are volunteers and already have a heavy review burden. While we will try to triage and fix issues in a timely manner, we cannot guarantee any fixed timeline for issue resolution. +- While modern industry practices around coordinated disclosures encourage public disclosure to avoid vendors stonewalling researchers, we are an open source project that would gain little from needlessly stonewalling researchers. We thus kindly request that reporters do not publicly disclose issues they have reported to us before an agreed-to disclosure date. From 6dc9188a6bed5edf23483f5c474d3cd241fb22fc Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sat, 11 Apr 2026 22:15:52 +0100 Subject: [PATCH 25/29] nss-myhostname: add more INC_SAFE for buffer index accumulation Use overflow-safe INC_SAFE() instead of raw addition for idx accumulation, so that Coverity can see the addition is checked. CID#1548028 Follow-up for a05483a921a518fd283e7cb32dc8c8e816b2ab2c (cherry picked from commit 1afc0c6c608e75e6fccba13cc4f36039b0a7ae6e) --- src/nss-myhostname/nss-myhostname.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c index ed470ed298cc4..c28c5161e1603 100644 --- a/src/nss-myhostname/nss-myhostname.c +++ b/src/nss-myhostname/nss-myhostname.c @@ -230,7 +230,7 @@ static enum nss_status fill_in_hostent( if (additional) { r_alias = buffer + idx; memcpy(r_alias, additional, l_additional+1); - idx += ALIGN(l_additional+1); + assert_se(INC_SAFE(&idx, ALIGN(l_additional+1))); } /* Second, create aliases array */ @@ -258,14 +258,14 @@ static enum nss_status fill_in_hostent( } assert(i == c); - idx += c*ALIGN(alen); + assert_se(INC_SAFE(&idx, c*ALIGN(alen))); } else if (af == AF_INET) { *(uint32_t*) r_addr = local_address_ipv4; - idx += ALIGN(alen); + assert_se(INC_SAFE(&idx, ALIGN(alen))); } else if (socket_ipv6_is_enabled()) { memcpy(r_addr, LOCALADDRESS_IPV6, FAMILY_ADDRESS_SIZE(AF_INET6)); - idx += ALIGN(alen); + assert_se(INC_SAFE(&idx, ALIGN(alen))); } /* Fourth, add address pointer array */ @@ -277,15 +277,15 @@ static enum nss_status fill_in_hostent( ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen); ((char**) r_addr_list)[i] = NULL; - idx += (c+1) * sizeof(char*); + assert_se(INC_SAFE(&idx, (c+1) * sizeof(char*))); } else if (af == AF_INET || socket_ipv6_is_enabled()) { ((char**) r_addr_list)[0] = r_addr; ((char**) r_addr_list)[1] = NULL; - idx += 2 * sizeof(char*); + assert_se(INC_SAFE(&idx, 2 * sizeof(char*))); } else { ((char**) r_addr_list)[0] = NULL; - idx += sizeof(char*); + assert_se(INC_SAFE(&idx, sizeof(char*))); } /* Verify the size matches */ From ac6c771f99960d14c1252e2e586102b36f4aaed6 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sat, 11 Apr 2026 22:52:47 +0100 Subject: [PATCH 26/29] test-json: add iszero_safe guards for float division at index 0 and 1 The existing iszero_safe guards at index 9 and 10 were added to silence Coverity, but the same division-by-float-zero warning also applies to the divisions at index 0 (DBL_MIN) and 1 (DBL_MAX). CID#1587762 Follow-up for 7f133c996c8b1ea9219540ec8f966b64b58d30a6 (cherry picked from commit 44296e41db20b40d0b9a4cbe320d262ffdd8905d) --- src/test/test-json.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/test-json.c b/src/test/test-json.c index f10bf9c7cad68..c95c84846b460 100644 --- a/src/test/test-json.c +++ b/src/test/test-json.c @@ -689,7 +689,9 @@ static void test_float_match(sd_json_variant *v) { assert_se(sd_json_variant_is_array(v)); assert_se(sd_json_variant_elements(v) == 11); + assert_se(!iszero_safe(sd_json_variant_real(sd_json_variant_by_index(v, 0)))); assert_se(fabs(1.0 - (DBL_MIN / sd_json_variant_real(sd_json_variant_by_index(v, 0)))) <= delta); + assert_se(!iszero_safe(sd_json_variant_real(sd_json_variant_by_index(v, 1)))); assert_se(fabs(1.0 - (DBL_MAX / sd_json_variant_real(sd_json_variant_by_index(v, 1)))) <= delta); assert_se(sd_json_variant_is_null(sd_json_variant_by_index(v, 2))); /* nan is not supported by json → null */ assert_se(sd_json_variant_is_null(sd_json_variant_by_index(v, 3))); /* +inf is not supported by json → null */ From b47ba1983f22bedce6c656fd460f07bdb5be600e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 11 Apr 2026 13:06:56 +0200 Subject: [PATCH 27/29] fundamental: add ABS_DIFF macro Sometimes we want need to diff two unsigned numbers, which is awkward because we need to cast them to something with a sign first, if we want to use abs(). Let's add a helper that avoids the function call altogether. Also drop unnecessary parens arounds args which are delimited by commas. (cherry picked from commit efbd8a26d65c68a16c64e3d8cb5ae9d298ac6abb) --- src/fundamental/macro-fundamental.h | 20 ++++++++++++++------ src/test/test-macro.c | 7 +++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h index e8757b1fc37a4..c4ad848b6f54e 100644 --- a/src/fundamental/macro-fundamental.h +++ b/src/fundamental/macro-fundamental.h @@ -160,7 +160,7 @@ #define U64_GB (UINT64_C(1024) * U64_MB) #undef MAX -#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) +#define MAX(a, b) __MAX(UNIQ, a, UNIQ, b) #define __MAX(aq, a, bq, b) \ ({ \ const typeof(a) UNIQ_T(A, aq) = (a); \ @@ -217,7 +217,7 @@ assert_cc(sizeof(long long) == sizeof(intmax_t)); }) #undef MIN -#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b)) +#define MIN(a, b) __MIN(UNIQ, a, UNIQ, b) #define __MIN(aq, a, bq, b) \ ({ \ const typeof(a) UNIQ_T(A, aq) = (a); \ @@ -225,6 +225,14 @@ assert_cc(sizeof(long long) == sizeof(intmax_t)); UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ }) +#define ABS_DIFF(a, b) __ABS_DIFF(UNIQ, a, UNIQ, b) +#define __ABS_DIFF(aq, a, bq, b) \ + ({ \ + const typeof(a) UNIQ_T(A, aq) = (a); \ + const typeof(b) UNIQ_T(B, bq) = (b); \ + UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(B, bq) - UNIQ_T(A, aq) : UNIQ_T(A, aq) - UNIQ_T(B, bq); \ + }) + /* evaluates to (void) if _A or _B are not constant or of different types */ #define CONST_MIN(_A, _B) \ (__builtin_choose_expr( \ @@ -295,7 +303,7 @@ assert_cc(sizeof(long long) == sizeof(intmax_t)); }) #undef CLAMP -#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high)) +#define CLAMP(x, low, high) __CLAMP(UNIQ, x, UNIQ, low, UNIQ, high) #define __CLAMP(xq, x, lowq, low, highq, high) \ ({ \ const typeof(x) UNIQ_T(X, xq) = (x); \ @@ -312,7 +320,7 @@ assert_cc(sizeof(long long) == sizeof(intmax_t)); * computation should be possible in the given type. Therefore, we use * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the * quotient and the remainder, so both should be equally fast. */ -#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y)) +#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, x, UNIQ, y) #define __DIV_ROUND_UP(xq, x, yq, y) \ ({ \ const typeof(x) UNIQ_T(X, xq) = (x); \ @@ -324,11 +332,11 @@ assert_cc(sizeof(long long) == sizeof(intmax_t)); #define __ROUND_UP(q, x, y) \ ({ \ const typeof(y) UNIQ_T(A, q) = (y); \ - const typeof(x) UNIQ_T(B, q) = DIV_ROUND_UP((x), UNIQ_T(A, q)); \ + const typeof(x) UNIQ_T(B, q) = DIV_ROUND_UP(x, UNIQ_T(A, q)); \ typeof(x) UNIQ_T(C, q); \ MUL_SAFE(&UNIQ_T(C, q), UNIQ_T(B, q), UNIQ_T(A, q)) ? UNIQ_T(C, q) : (typeof(x)) -1; \ }) -#define ROUND_UP(x, y) __ROUND_UP(UNIQ, (x), (y)) +#define ROUND_UP(x, y) __ROUND_UP(UNIQ, x, y) #define CASE_F_1(X) case X: #define CASE_F_2(X, ...) case X: CASE_F_1( __VA_ARGS__) diff --git a/src/test/test-macro.c b/src/test/test-macro.c index 7f7bf1ce8dbda..9a9a1fa2dac7f 100644 --- a/src/test/test-macro.c +++ b/src/test/test-macro.c @@ -130,6 +130,13 @@ TEST(MAX) { assert_se(CLAMP(CLAMP(0, -10, 10), CLAMP(-5, 10, 20), CLAMP(100, -5, 20)) == 10); } +TEST(ABS_DIFF) { + ASSERT_EQ(ABS_DIFF(5, 3), 2); + ASSERT_EQ(ABS_DIFF(3, 5), 2); + ASSERT_EQ(ABS_DIFF(5llu, 2llu), 3llu); + ASSERT_EQ(ABS_DIFF(3llu, 5llu), 2llu); +} + #pragma GCC diagnostic push #ifdef __clang__ # pragma GCC diagnostic ignored "-Waddress-of-packed-member" From 074f90bf958ac8e9875b59d193753b850e531cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 11 Apr 2026 13:09:16 +0200 Subject: [PATCH 28/29] homed: drop unnecessary cast to double Coverity was complaining that we we're doing a integer division and then casting that to double. This was OK, but it was also a bit pointless. An operation on a double and unsigned promoted the unsigned to a double, so it's enough if we have a double somewhere as an argument early enough. Drop noop casts and parens to make the formulas easier to read. CID#1466459 (cherry picked from commit 3fac59557cf15bc142b9a2f43e4cfbe99d1a6190) --- src/home/homed-manager.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c index 85c92192f483d..f33bd79b9c1d7 100644 --- a/src/home/homed-manager.c +++ b/src/home/homed-manager.c @@ -1889,10 +1889,10 @@ static int manager_rebalance_calculate(Manager *m) { assert(h->rebalance_usage <= usage_sum); assert(h->rebalance_weight <= weight_sum); - d = ((double) (free_sum / 4096.0) * (double) h->rebalance_weight) / (double) weight_sum; /* Calculate new space for this home in units of 4K */ + d = free_sum / 4096.0 * h->rebalance_weight / weight_sum; /* Calculate new space for this home in units of 4K */ /* Convert from units of 4K back to bytes */ - if (d >= (double) (UINT64_MAX/4096)) + if (d >= UINT64_MAX / 4096) new_free = UINT64_MAX; else new_free = (uint64_t) d * 4096; @@ -1928,7 +1928,7 @@ static int manager_rebalance_calculate(Manager *m) { h->rebalance_pending = true; } - if ((fabs((double) h->rebalance_size - (double) h->rebalance_goal) * 100 / (double) h->rebalance_size) >= 5.0) + if (ABS_DIFF(h->rebalance_size, h->rebalance_goal) * 100.0 / h->rebalance_size >= 5.0) relevant = true; } From 44ed48d805d52c9800ecb08fd8e3760e0794454d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 13 Apr 2026 10:29:11 +0200 Subject: [PATCH 29/29] core: use JSON_BUILD_CONST_STRING() where appropriate (cherry picked from commit 087733e348f060b1c79cf72c9615c706c2c9d851) --- src/core/varlink-unit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/varlink-unit.c b/src/core/varlink-unit.c index d222148c77c3d..daaf5cb5b5aea 100644 --- a/src/core/varlink-unit.c +++ b/src/core/varlink-unit.c @@ -225,7 +225,7 @@ static int can_clean_build_json(sd_json_variant **ret, const char *name, void *u } if (FLAGS_SET(mask, EXEC_CLEAN_FDSTORE)) { - r = sd_json_variant_append_arrayb(&v, SD_JSON_BUILD_STRING("fdstore")); + r = sd_json_variant_append_arrayb(&v, JSON_BUILD_CONST_STRING("fdstore")); if (r < 0) return r; }