From 20558ddb5aa37f2e511d787c9f1be1152985865c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 18 Apr 2026 07:19:27 +0900 Subject: [PATCH 1/6] iovec-util: introduce iovec_equal() --- src/basic/iovec-util.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/basic/iovec-util.h b/src/basic/iovec-util.h index 00cbb89a77..21aad9edfc 100644 --- a/src/basic/iovec-util.h +++ b/src/basic/iovec-util.h @@ -38,6 +38,9 @@ char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const ch void iovec_array_free(struct iovec *iovec, size_t n_iovec) _nonnull_if_nonzero_(1, 2); int iovec_memcmp(const struct iovec *a, const struct iovec *b) _pure_; +static inline bool iovec_equal(const struct iovec *a, const struct iovec *b) { + return iovec_memcmp(a, b) == 0; +} struct iovec* iovec_memdup(const struct iovec *source, struct iovec *ret); From 036ea5b14ff6e9c9a1283dc4e00d17048bb60456 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 19 Apr 2026 03:30:49 +0900 Subject: [PATCH 2/6] tree-wide: use iovec_equal() --- coccinelle/memcmp.cocci | 19 ++++++++++++++++++ src/core/namespace.c | 2 +- src/cryptenroll/cryptenroll-tpm2.c | 6 +++--- src/import/pull-job.c | 2 +- src/import/pull-oci.c | 2 +- src/shared/dissect-image.c | 6 +++--- src/shared/tpm2-util.c | 6 +++--- src/sysupdate/sysupdate-resource.c | 2 +- src/test/test-creds.c | 2 +- src/test/test-fileio.c | 10 +++++----- src/test/test-iovec-wrapper.c | 32 +++++++++++++++--------------- src/test/test-json.c | 4 ++-- src/test/test-log.c | 10 +++++----- src/test/test-tpm2.c | 2 +- 14 files changed, 62 insertions(+), 43 deletions(-) create mode 100644 coccinelle/memcmp.cocci diff --git a/coccinelle/memcmp.cocci b/coccinelle/memcmp.cocci new file mode 100644 index 0000000000..aa0effa4f8 --- /dev/null +++ b/coccinelle/memcmp.cocci @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* Disable this transformation for iovec-util.h and the unit test */ +@ depends on !(file in "src/basic/iovec-util.h") + && !(file in "src/test/test-iovec-util.c") @ +expression a, b; +@@ +( +- iovec_memcmp(a, b) == 0 ++ iovec_equal(a, b) +| +- iovec_memcmp(a, b) != 0 ++ !iovec_equal(a, b) +| +- ASSERT_EQ(iovec_memcmp(a, b), 0) ++ ASSERT_TRUE(iovec_equal(a, b)) +| +- ASSERT_NE(iovec_memcmp(a, b), 0) ++ ASSERT_FALSE(iovec_equal(a, b)) +) diff --git a/src/core/namespace.c b/src/core/namespace.c index ff4592b8b9..bd27cbe2b5 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -1049,7 +1049,7 @@ static bool verity_has_later_duplicates(MountList *ml, const MountEntry *needle) for (const MountEntry *m = needle + 1; m < ml->mounts + ml->n_mounts; m++) { if (m->mode != MOUNT_EXTENSION_IMAGE) continue; - if (iovec_memcmp(&m->verity.root_hash, &needle->verity.root_hash) == 0) + if (iovec_equal(&m->verity.root_hash, &needle->verity.root_hash)) return true; } diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c index 50abca4363..9badd9fd67 100644 --- a/src/cryptenroll/cryptenroll-tpm2.c +++ b/src/cryptenroll/cryptenroll-tpm2.c @@ -72,7 +72,7 @@ static int search_policy_hash( if (r < 0) return log_error_errno(r, "Invalid hex data in 'tpm2-policy-hash' field item : %m"); - if (iovec_memcmp(policy_hash + j, &thash) != 0) { + if (!iovec_equal(policy_hash + j, &thash)) { match = false; break; } @@ -91,7 +91,7 @@ static int search_policy_hash( if (r < 0) return log_error_errno(r, "Invalid hex data in 'tpm2-policy-hash' field: %m"); - if (iovec_memcmp(policy_hash + 0, &thash) == 0) + if (iovec_equal(policy_hash + 0, &thash)) return keyslot; /* Found entry with same hash. */ } } @@ -566,7 +566,7 @@ int enroll_tpm2(struct crypt_device *cd, if (r < 0) return log_error_errno(r, "Failed to unseal secret using TPM2: %m"); - if (iovec_memcmp(&secret, &secret2) != 0) + if (!iovec_equal(&secret, &secret2)) return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 seal/unseal verification failed."); } diff --git a/src/import/pull-job.c b/src/import/pull-job.c index 385043dda8..e847d5e2fb 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -298,7 +298,7 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { } if (iovec_is_set(&j->expected_checksum) && - iovec_memcmp(&j->checksum, &j->expected_checksum) != 0) { + !iovec_equal(&j->checksum, &j->expected_checksum)) { r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Checksum of downloaded resource does not match expected checksum, yikes."); goto finish; } diff --git a/src/import/pull-oci.c b/src/import/pull-oci.c index 4ae933928f..f4878e3c87 100644 --- a/src/import/pull-oci.c +++ b/src/import/pull-oci.c @@ -1195,7 +1195,7 @@ static int oci_pull_make_local(OciPull *i) { return r; if (!iovec_is_set(&i->config) || - iovec_memcmp(&i->config, &IOVEC_MAKE_STRING("{}")) == 0) + iovec_equal(&i->config, &IOVEC_MAKE_STRING("{}"))) log_info("Image has no configuration, not saving."); else { r = oci_pull_save_nspawn_settings(i); diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index e33e78d310..894fabf7a6 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -1422,7 +1422,7 @@ static int dissect_image( /* ret_root_hash_sig= */ NULL); if (r < 0) return r; - if (iovec_memcmp(&verity->root_hash, &root_hash) != 0) { + if (!iovec_equal(&verity->root_hash, &root_hash)) { if (DEBUG_LOGGING) { _cleanup_free_ char *found = NULL, *expected = NULL; @@ -3023,7 +3023,7 @@ static int verity_can_reuse( r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, root_hash_existing.iov_base, &root_hash_existing.iov_len, NULL, 0); if (r < 0) return log_debug_errno(r, "Error opening verity device, crypt_volume_key_get failed: %m"); - if (iovec_memcmp(&verity->root_hash, &root_hash_existing) != 0) + if (!iovec_equal(&verity->root_hash, &root_hash_existing)) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but root hashes are different."); /* Ensure that, if signatures are supported, we only reuse the device if the previous mount used the @@ -4048,7 +4048,7 @@ int dissected_image_load_verity_sig_partition( /* Check if specified root hash matches if it is specified */ if (iovec_is_set(&verity->root_hash) && - iovec_memcmp(&verity->root_hash, &root_hash) != 0) { + !iovec_equal(&verity->root_hash, &root_hash)) { _cleanup_free_ char *a = NULL, *b = NULL; a = hexmem(root_hash.iov_base, root_hash.iov_len); diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 02f89c02ba..bddf7a74bf 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -7176,7 +7176,7 @@ static int tpm2_nvpcr_write_anchor_secret( if (r < 0) { if (r != -ENOENT) return log_error_errno(r, "Failed to read '%s' file: %m", joined); - } else if (iovec_memcmp(&existing, credential) == 0) { + } else if (iovec_equal(&existing, credential)) { log_debug("Anchor secret file '%s' already matches expectations, not updating.", joined); return 0; } else @@ -8460,8 +8460,8 @@ int tpm2_pcrlock_policy_from_credentials( continue; } - if ((!srk || iovec_memcmp(srk, &loaded_policy.srk_handle) == 0) && - (!nv || iovec_memcmp(nv, &loaded_policy.nv_handle) == 0)) { + if ((!srk || iovec_equal(srk, &loaded_policy.srk_handle)) && + (!nv || iovec_equal(nv, &loaded_policy.nv_handle))) { *ret = TAKE_STRUCT(loaded_policy); return 1; } diff --git a/src/sysupdate/sysupdate-resource.c b/src/sysupdate/sysupdate-resource.c index b819fcd5b8..a9ef81f71d 100644 --- a/src/sysupdate/sysupdate-resource.c +++ b/src/sysupdate/sysupdate-resource.c @@ -430,7 +430,7 @@ static int process_magic_file( /* Even if we ignore if people have non-empty files for this file, let's nonetheless warn about it, * so that people fix it. After all we want to retain liberty to maybe one day place some useful data * inside it */ - if (iovec_memcmp(&IOVEC_MAKE(expected_hash, sizeof(expected_hash)), hash) != 0) + if (!iovec_equal(&IOVEC_MAKE(expected_hash, sizeof(expected_hash)), hash)) log_warning("Hash of best before marker file '%s' has unexpected value, proceeding anyway.", fn); usec_t best_before; diff --git a/src/test/test-creds.c b/src/test/test-creds.c index 5cad608093..e380550a5a 100644 --- a/src/test/test-creds.c +++ b/src/test/test-creds.c @@ -177,7 +177,7 @@ static void test_encrypt_decrypt_with(sd_id128_t mode, uid_t uid) { &decrypted); ASSERT_OK(r); - ASSERT_EQ(iovec_memcmp(&plaintext, &decrypted), 0); + ASSERT_TRUE(iovec_equal(&plaintext, &decrypted)); } static bool try_tpm2(void) { diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 575e2c52ed..a752b1c7cd 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -702,13 +702,13 @@ TEST(write_data_file_atomic_at) { _cleanup_(iovec_done) struct iovec ra = {}; ASSERT_OK(read_full_file("/tmp/wdfa", (char**) &ra.iov_base, &ra.iov_len)); - ASSERT_EQ(iovec_memcmp(&a, &ra), 0); + ASSERT_TRUE(iovec_equal(&a, &ra)); ASSERT_OK_ERRNO(unlink("/tmp/wdfa")); ASSERT_OK(write_data_file_atomic_at(XAT_FDROOT, "tmp/wdfa", &a, /* flags= */ 0)); iovec_done(&ra); ASSERT_OK(read_full_file("/tmp/wdfa", (char**) &ra.iov_base, &ra.iov_len)); - ASSERT_EQ(iovec_memcmp(&a, &ra), 0); + ASSERT_TRUE(iovec_equal(&a, &ra)); ASSERT_OK_ERRNO(unlink("/tmp/wdfa")); ASSERT_ERROR(write_data_file_atomic_at(AT_FDCWD, NULL, &a, /* flags= */ 0), EINVAL); @@ -724,13 +724,13 @@ TEST(write_data_file_atomic_at) { ASSERT_OK(write_data_file_atomic_at(AT_FDCWD, "wdfa", &a, /* flags= */ 0)); iovec_done(&ra); ASSERT_OK(read_full_file("/tmp/wdfa", (char**) &ra.iov_base, &ra.iov_len)); - ASSERT_EQ(iovec_memcmp(&a, &ra), 0); + ASSERT_TRUE(iovec_equal(&a, &ra)); ASSERT_OK_ERRNO(unlink("/tmp/wdfa")); ASSERT_OK(write_data_file_atomic_at(XAT_FDROOT, "tmp/wdfa", &a, /* flags= */ 0)); iovec_done(&ra); ASSERT_OK(read_full_file("/tmp/wdfa", (char**) &ra.iov_base, &ra.iov_len)); - ASSERT_EQ(iovec_memcmp(&a, &ra), 0); + ASSERT_TRUE(iovec_equal(&a, &ra)); ASSERT_OK_ERRNO(unlink("/tmp/wdfa")); ASSERT_OK_ERRNO(chdir(cwd)); @@ -739,7 +739,7 @@ TEST(write_data_file_atomic_at) { ASSERT_OK(write_data_file_atomic_at(XAT_FDROOT, "tmp/zzz/wdfa", &a, WRITE_DATA_FILE_MKDIR_0755)); iovec_done(&ra); ASSERT_OK(read_full_file("/tmp/zzz/wdfa", (char**) &ra.iov_base, &ra.iov_len)); - ASSERT_EQ(iovec_memcmp(&a, &ra), 0); + ASSERT_TRUE(iovec_equal(&a, &ra)); ASSERT_OK_ERRNO(unlink("/tmp/zzz/wdfa")); ASSERT_ERROR(write_data_file_atomic_at(AT_FDCWD, "/tmp/zzz", &a, /* flags= */ 0), EEXIST); diff --git a/src/test/test-iovec-wrapper.c b/src/test/test-iovec-wrapper.c index 6764d2e78e..791a041ab8 100644 --- a/src/test/test-iovec-wrapper.c +++ b/src/test/test-iovec-wrapper.c @@ -97,9 +97,9 @@ TEST(iovw_put_iov) { ASSERT_OK(iovw_put_iov(&iovw, &IOVEC_MAKE_STRING("bbb"))); ASSERT_OK(iovw_put_iov(&iovw, &IOVEC_MAKE_STRING("ccc"))); ASSERT_EQ(iovw.count, 3U); - ASSERT_EQ(iovec_memcmp(&iovw.iovec[0], &IOVEC_MAKE_STRING("aaa")), 0); - ASSERT_EQ(iovec_memcmp(&iovw.iovec[1], &IOVEC_MAKE_STRING("bbb")), 0); - ASSERT_EQ(iovec_memcmp(&iovw.iovec[2], &IOVEC_MAKE_STRING("ccc")), 0); + ASSERT_TRUE(iovec_equal(&iovw.iovec[0], &IOVEC_MAKE_STRING("aaa"))); + ASSERT_TRUE(iovec_equal(&iovw.iovec[1], &IOVEC_MAKE_STRING("bbb"))); + ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &IOVEC_MAKE_STRING("ccc"))); } TEST(iovw_put_iovw) { @@ -123,12 +123,12 @@ TEST(iovw_put_iovw) { ASSERT_OK(iovw_put_iovw(&target, &source)); ASSERT_EQ(target.count, 6U); - ASSERT_EQ(iovec_memcmp(&target.iovec[0], &IOVEC_MAKE_STRING("xxx")), 0); - ASSERT_EQ(iovec_memcmp(&target.iovec[1], &IOVEC_MAKE_STRING("yyy")), 0); - ASSERT_EQ(iovec_memcmp(&target.iovec[2], &IOVEC_MAKE_STRING("zzz")), 0); - ASSERT_EQ(iovec_memcmp(&target.iovec[3], &IOVEC_MAKE_STRING("aaa")), 0); - ASSERT_EQ(iovec_memcmp(&target.iovec[4], &IOVEC_MAKE_STRING("bbb")), 0); - ASSERT_EQ(iovec_memcmp(&target.iovec[5], &IOVEC_MAKE_STRING("ccc")), 0); + ASSERT_TRUE(iovec_equal(&target.iovec[0], &IOVEC_MAKE_STRING("xxx"))); + ASSERT_TRUE(iovec_equal(&target.iovec[1], &IOVEC_MAKE_STRING("yyy"))); + ASSERT_TRUE(iovec_equal(&target.iovec[2], &IOVEC_MAKE_STRING("zzz"))); + ASSERT_TRUE(iovec_equal(&target.iovec[3], &IOVEC_MAKE_STRING("aaa"))); + ASSERT_TRUE(iovec_equal(&target.iovec[4], &IOVEC_MAKE_STRING("bbb"))); + ASSERT_TRUE(iovec_equal(&target.iovec[5], &IOVEC_MAKE_STRING("ccc"))); /* iovw_put_iovw() does not copy data, hence the pointers must be equal */ ASSERT_PTR_EQ(target.iovec[3].iov_base, source.iovec[0].iov_base); @@ -137,9 +137,9 @@ TEST(iovw_put_iovw) { /* Source is unchanged */ ASSERT_EQ(source.count, 3U); - ASSERT_EQ(iovec_memcmp(&source.iovec[0], &IOVEC_MAKE_STRING("aaa")), 0); - ASSERT_EQ(iovec_memcmp(&source.iovec[1], &IOVEC_MAKE_STRING("bbb")), 0); - ASSERT_EQ(iovec_memcmp(&source.iovec[2], &IOVEC_MAKE_STRING("ccc")), 0); + ASSERT_TRUE(iovec_equal(&source.iovec[0], &IOVEC_MAKE_STRING("aaa"))); + ASSERT_TRUE(iovec_equal(&source.iovec[1], &IOVEC_MAKE_STRING("bbb"))); + ASSERT_TRUE(iovec_equal(&source.iovec[2], &IOVEC_MAKE_STRING("ccc"))); /* Cannot pass the same objects */ ASSERT_ERROR(iovw_put_iovw(&target, &target), EINVAL); @@ -183,9 +183,9 @@ TEST(iovw_extend_iov) { ASSERT_OK(iovw_extend_iov(&iovw, &IOVEC_MAKE_STRING("bbb"))); ASSERT_OK(iovw_extend_iov(&iovw, &IOVEC_MAKE_STRING("ccc"))); ASSERT_EQ(iovw.count, 3U); - ASSERT_EQ(iovec_memcmp(&iovw.iovec[0], &IOVEC_MAKE_STRING("aaa")), 0); - ASSERT_EQ(iovec_memcmp(&iovw.iovec[1], &IOVEC_MAKE_STRING("bbb")), 0); - ASSERT_EQ(iovec_memcmp(&iovw.iovec[2], &IOVEC_MAKE_STRING("ccc")), 0); + ASSERT_TRUE(iovec_equal(&iovw.iovec[0], &IOVEC_MAKE_STRING("aaa"))); + ASSERT_TRUE(iovec_equal(&iovw.iovec[1], &IOVEC_MAKE_STRING("bbb"))); + ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &IOVEC_MAKE_STRING("ccc"))); } TEST(iovw_extend_iovw) { @@ -410,7 +410,7 @@ TEST(iovw_concat) { ASSERT_OK(iovw_put(&iovw, (char*) "bar", 4)); ASSERT_OK(iovw_concat(&iovw, &iov)); - ASSERT_EQ(iovec_memcmp(&iov, &IOVEC_MAKE("foo\0bar\0", 8)), 0); + ASSERT_TRUE(iovec_equal(&iov, &IOVEC_MAKE("foo\0bar\0", 8))); } TEST(iovw_to_cstring) { diff --git a/src/test/test-json.c b/src/test/test-json.c index 1bd5d94f98..1785ef416f 100644 --- a/src/test/test-json.c +++ b/src/test/test-json.c @@ -1206,8 +1206,8 @@ TEST(json_iovec) { assert_se(json_variant_unbase64_iovec(sd_json_variant_by_key(j, "nr1"), &a) >= 0); assert_se(json_variant_unhex_iovec(sd_json_variant_by_key(j, "nr2"), &b) >= 0); - assert_se(iovec_memcmp(&iov1, &a) == 0); - assert_se(iovec_memcmp(&iov2, &b) == 0); + assert_se(iovec_equal(&iov1, &a)); + assert_se(iovec_equal(&iov2, &b)); assert_se(iovec_memcmp(&iov2, &a) < 0); assert_se(iovec_memcmp(&iov1, &b) > 0); } diff --git a/src/test/test-log.c b/src/test/test-log.c index a62f54006e..fb23776b21 100644 --- a/src/test/test-log.c +++ b/src/test/test-log.c @@ -104,9 +104,9 @@ static void test_log_format_iovec_sentinel( for (size_t i = 0; i < n; i++) if (i < m) - ASSERT_EQ(iovec_memcmp(&iovec[i], &IOVEC_MAKE_STRING(v[i])), 0); + ASSERT_TRUE(iovec_equal(&iovec[i], &IOVEC_MAKE_STRING(v[i]))); else { - ASSERT_EQ(iovec_memcmp(&iovec[i], &IOVEC_MAKE_STRING(expected[i - m])), 0); + ASSERT_TRUE(iovec_equal(&iovec[i], &IOVEC_MAKE_STRING(expected[i - m]))); free(iovec[i].iov_base); } @@ -122,12 +122,12 @@ static void test_log_format_iovec_sentinel( for (size_t i = 0; i < n; i++) if (i < m) - ASSERT_EQ(iovec_memcmp(&iovec[i], &IOVEC_MAKE_STRING(v[i])), 0); + ASSERT_TRUE(iovec_equal(&iovec[i], &IOVEC_MAKE_STRING(v[i]))); else if ((i - m) % 2 == 0) { - ASSERT_EQ(iovec_memcmp(&iovec[i], &IOVEC_MAKE_STRING(expected[(i - m) / 2])), 0); + ASSERT_TRUE(iovec_equal(&iovec[i], &IOVEC_MAKE_STRING(expected[(i - m) / 2]))); free(iovec[i].iov_base); } else - ASSERT_EQ(iovec_memcmp(&iovec[i], &IOVEC_MAKE_STRING("\n")), 0); + ASSERT_TRUE(iovec_equal(&iovec[i], &IOVEC_MAKE_STRING("\n"))); } #define test_log_format_iovec_one(...) \ diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c index a6164f2677..78e2d234ab 100644 --- a/src/test/test-tpm2.c +++ b/src/test/test-tpm2.c @@ -1271,7 +1271,7 @@ static void check_seal_unseal_for_handle(Tpm2Context *c, TPM2_HANDLE handle) { &srk, &unsealed_secret) >= 0); - assert_se(iovec_memcmp(&secret, &unsealed_secret) == 0); + assert_se(iovec_equal(&secret, &unsealed_secret)); } static void check_seal_unseal(Tpm2Context *c) { From 16f5a2992d3babdcbba319abaf9617db5ce1c924 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 19 Apr 2026 07:33:17 +0900 Subject: [PATCH 3/6] resolve: add missing OOM check --- src/resolve/resolved-dnstls.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/resolve/resolved-dnstls.c b/src/resolve/resolved-dnstls.c index 09dda4508b..bfc6a72183 100644 --- a/src/resolve/resolved-dnstls.c +++ b/src/resolve/resolved-dnstls.c @@ -333,6 +333,9 @@ ssize_t dnstls_stream_writev(DnsStream *stream, const struct iovec *iov, size_t single buffer. Suboptimal, but better than multiple SSL_write calls. */ count = iovec_total_size(iov, iovcnt); buf = new(char, count); + if (!buf) + return -ENOMEM; + for (size_t i = 0, pos = 0; i < iovcnt; pos += iov[i].iov_len, i++) memcpy(buf + pos, iov[i].iov_base, iov[i].iov_len); From 9628b3118628343f6e156d49b9f2de040f6d675b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 18 Apr 2026 07:21:38 +0900 Subject: [PATCH 4/6] iovec-util: add overflow check in iovec_total_size() Mostly theoretical. But for safety. --- src/basic/iovec-util.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/basic/iovec-util.c b/src/basic/iovec-util.c index ec8af57e64..cd2c1a736e 100644 --- a/src/basic/iovec-util.c +++ b/src/basic/iovec-util.c @@ -21,8 +21,11 @@ size_t iovec_total_size(const struct iovec *iovec, size_t n) { assert(iovec || n == 0); - FOREACH_ARRAY(j, iovec, n) + FOREACH_ARRAY(j, iovec, n) { + if (j->iov_len > SIZE_MAX - sum) + return SIZE_MAX; /* Indicate overflow. */ sum += j->iov_len; + } return sum; } From fa021e57513cf573d8cc932d90f0bd86a486589f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 19 Apr 2026 03:41:42 +0900 Subject: [PATCH 5/6] tree-wide: check result of iovec_total_size() --- src/libsystemd/sd-bus/bus-message.c | 20 ++++++++++---------- src/resolve/resolved-dnstls.c | 15 ++++++++------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index 041dc48216..94be969f7f 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -1498,9 +1498,6 @@ _public_ int sd_bus_message_append_string_iovec( const struct iovec *iov, unsigned n /* should be size_t, but is API now… 😞 */) { - size_t size; - unsigned i; - char *p; int r; assert_return(m, -EINVAL); @@ -1508,13 +1505,16 @@ _public_ int sd_bus_message_append_string_iovec( assert_return(iov || n == 0, -EINVAL); assert_return(!m->poisoned, -ESTALE); - size = iovec_total_size(iov, n); + size_t size = iovec_total_size(iov, n); + if (size == SIZE_MAX) + return -ENOBUFS; + char *p; r = sd_bus_message_append_string_space(m, size, &p); if (r < 0) return r; - for (i = 0; i < n; i++) { + for (unsigned i = 0; i < n; i++) { if (iov[i].iov_base) memcpy(p, iov[i].iov_base, iov[i].iov_len); @@ -2160,9 +2160,6 @@ _public_ int sd_bus_message_append_array_iovec( const struct iovec *iov, unsigned n /* should be size_t, but is API now… 😞 */) { - size_t size; - unsigned i; - void *p; int r; assert_return(m, -EINVAL); @@ -2171,13 +2168,16 @@ _public_ int sd_bus_message_append_array_iovec( assert_return(iov || n == 0, -EINVAL); assert_return(!m->poisoned, -ESTALE); - size = iovec_total_size(iov, n); + size_t size = iovec_total_size(iov, n); + if (size == SIZE_MAX) + return -ENOBUFS; + void *p; r = sd_bus_message_append_array_space(m, type, size, &p); if (r < 0) return r; - for (i = 0; i < n; i++) { + for (unsigned i = 0; i < n; i++) { if (iov[i].iov_base) memcpy(p, iov[i].iov_base, iov[i].iov_len); diff --git a/src/resolve/resolved-dnstls.c b/src/resolve/resolved-dnstls.c index bfc6a72183..042077ab06 100644 --- a/src/resolve/resolved-dnstls.c +++ b/src/resolve/resolved-dnstls.c @@ -317,29 +317,30 @@ static ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t co } ssize_t dnstls_stream_writev(DnsStream *stream, const struct iovec *iov, size_t iovcnt) { - _cleanup_free_ char *buf = NULL; - size_t count; - assert(stream); assert(stream->encrypted); assert(stream->dnstls_data.ssl); assert(iov); - assert(iovec_total_size(iov, iovcnt) > 0); + + size_t size = iovec_total_size(iov, iovcnt); + if (size == 0) + return -EINVAL; + if (size == SIZE_MAX) + return -ENOBUFS; if (iovcnt == 1) return dnstls_stream_write(stream, iov[0].iov_base, iov[0].iov_len); /* As of now, OpenSSL cannot accumulate multiple writes, so join into a single buffer. Suboptimal, but better than multiple SSL_write calls. */ - count = iovec_total_size(iov, iovcnt); - buf = new(char, count); + _cleanup_free_ char *buf = new(char, size); if (!buf) return -ENOMEM; for (size_t i = 0, pos = 0; i < iovcnt; pos += iov[i].iov_len, i++) memcpy(buf + pos, iov[i].iov_base, iov[i].iov_len); - return dnstls_stream_write(stream, buf, count); + return dnstls_stream_write(stream, buf, size); } ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) { From 5034852c6cd0b4957b4c074893c30005c76be73d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 19 Apr 2026 08:10:14 +0900 Subject: [PATCH 6/6] iovec-util: rename iovec_increment() -> iovec_inc_many() Also, - use iovec_inc() in the loop, - do not return true when input is NULL or all iovec are empty. --- src/basic/iovec-util.c | 15 ++++---- src/basic/iovec-util.h | 2 +- src/basic/log.c | 2 +- src/fuzz/fuzz-varlink.c | 2 +- src/libsystemd/sd-daemon/sd-daemon.c | 2 +- src/resolve/resolved-dns-stream.c | 2 +- src/test/test-iovec-util.c | 54 ++++++++++++++++++++++++++++ 7 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/basic/iovec-util.c b/src/basic/iovec-util.c index cd2c1a736e..dab734b901 100644 --- a/src/basic/iovec-util.c +++ b/src/basic/iovec-util.c @@ -30,29 +30,30 @@ size_t iovec_total_size(const struct iovec *iovec, size_t n) { return sum; } -bool iovec_increment(struct iovec *iovec, size_t n, size_t k) { +bool iovec_inc_many(struct iovec *iovec, size_t n, size_t k) { assert(iovec || n == 0); /* Returns true if there is nothing else to send (bytes written cover all of the iovec), * false if there's still work to do. */ + bool have = false; FOREACH_ARRAY(j, iovec, n) { - size_t sub; - if (j->iov_len == 0) continue; if (k == 0) return false; - sub = MIN(j->iov_len, k); - j->iov_len -= sub; - j->iov_base = (uint8_t*) j->iov_base + sub; + size_t sub = MIN(j->iov_len, k); + iovec_inc(j, sub); k -= sub; + + have = have || iovec_is_set(j); } assert(k == 0); /* Anything else would mean that we wrote more bytes than available, * or the kernel reported writing more bytes than sent. */ - return true; + + return !have; } struct iovec* iovec_make_string(struct iovec *iovec, const char *s) { diff --git a/src/basic/iovec-util.h b/src/basic/iovec-util.h index 21aad9edfc..c8261861a0 100644 --- a/src/basic/iovec-util.h +++ b/src/basic/iovec-util.h @@ -11,7 +11,7 @@ extern const struct iovec iovec_empty; /* Points to an empty, but valid (i.e. size_t iovec_total_size(const struct iovec *iovec, size_t n) _nonnull_if_nonzero_(1, 2); -bool iovec_increment(struct iovec *iovec, size_t n, size_t k) _nonnull_if_nonzero_(1, 2); +bool iovec_inc_many(struct iovec *iovec, size_t n, size_t k) _nonnull_if_nonzero_(1, 2); struct iovec* iovec_make_string(struct iovec *iovec, const char *s); diff --git a/src/basic/log.c b/src/basic/log.c index 473d0bd70f..d8b441bfad 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -557,7 +557,7 @@ static int write_to_syslog( if (!syslog_is_stream) break; - if (iovec_increment(iovec, ELEMENTSOF(iovec), n)) + if (iovec_inc_many(iovec, ELEMENTSOF(iovec), n)) break; } diff --git a/src/fuzz/fuzz-varlink.c b/src/fuzz/fuzz-varlink.c index fb1584a2ea..7bd7e5ab92 100644 --- a/src/fuzz/fuzz-varlink.c +++ b/src/fuzz/fuzz-varlink.c @@ -41,7 +41,7 @@ static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userd else assert_se(errno == EAGAIN); } else - iovec_increment(iov, 1, n); + iovec_inc(iov, n); } if (revents & EPOLLIN) { diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index 2937ac569c..da5242c3b7 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -621,7 +621,7 @@ static int pid_notify_with_fds_internal( msghdr.msg_control = NULL; msghdr.msg_controllen = 0; } - } while (!iovec_increment(msghdr.msg_iov, msghdr.msg_iovlen, n)); + } while (!iovec_inc_many(msghdr.msg_iov, msghdr.msg_iovlen, n)); if (address.sockaddr.sa.sa_family == AF_VSOCK && IN_SET(type, SOCK_STREAM, SOCK_SEQPACKET)) { /* For AF_VSOCK, we need to close the socket to signal the end of the message. */ diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c index e10605538a..b1d1b0069c 100644 --- a/src/resolve/resolved-dns-stream.c +++ b/src/resolve/resolved-dns-stream.c @@ -347,7 +347,7 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use IOVEC_MAKE(DNS_PACKET_DATA(s->write_packet), s->write_packet->size), }; - iovec_increment(iov, ELEMENTSOF(iov), s->n_written); + iovec_inc_many(iov, ELEMENTSOF(iov), s->n_written); ssize_t ss = dns_stream_writev(s, iov, ELEMENTSOF(iov), 0); if (ss < 0) { diff --git a/src/test/test-iovec-util.c b/src/test/test-iovec-util.c index 68255071e8..bd73be1ea7 100644 --- a/src/test/test-iovec-util.c +++ b/src/test/test-iovec-util.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "iovec-util.h" +#include "iovec-wrapper.h" #include "memory-util.h" #include "tests.h" @@ -38,6 +39,59 @@ TEST(iovec_inc) { ASSERT_FALSE(iovec_is_set(iovec_inc(&empty, 1))); } +TEST(iovec_inc_many) { + ASSERT_TRUE(iovec_inc_many(NULL, 0, 0)); + ASSERT_TRUE(iovec_inc_many(&(struct iovec) {}, 0, 0)); + ASSERT_TRUE(iovec_inc_many(&(struct iovec) {}, 1, 0)); + + _cleanup_(iovw_done) struct iovec_wrapper iovw = {}; + ASSERT_OK(iovw_put_iov(&iovw, &IOVEC_MAKE_STRING("aaa"))); + ASSERT_OK(iovw_put_iov(&iovw, &IOVEC_MAKE_STRING("bbb"))); + ASSERT_OK(iovw_put_iov(&iovw, &IOVEC_MAKE_STRING("ccc"))); + + ASSERT_FALSE(iovec_inc_many(iovw.iovec, iovw.count, 0)); + ASSERT_TRUE(iovec_equal(&iovw.iovec[0], &IOVEC_MAKE_STRING("aaa"))); + ASSERT_TRUE(iovec_equal(&iovw.iovec[1], &IOVEC_MAKE_STRING("bbb"))); + ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &IOVEC_MAKE_STRING("ccc"))); + + ASSERT_FALSE(iovec_inc_many(iovw.iovec, iovw.count, 1)); + ASSERT_TRUE(iovec_equal(&iovw.iovec[0], &IOVEC_MAKE_STRING("aa"))); + ASSERT_TRUE(iovec_equal(&iovw.iovec[1], &IOVEC_MAKE_STRING("bbb"))); + ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &IOVEC_MAKE_STRING("ccc"))); + + ASSERT_FALSE(iovec_inc_many(iovw.iovec, iovw.count, 3)); + ASSERT_FALSE(iovec_is_set(&iovw.iovec[0])); + ASSERT_NULL(iovw.iovec[0].iov_base); + ASSERT_EQ(iovw.iovec[0].iov_len, 0u); + ASSERT_TRUE(iovec_equal(&iovw.iovec[1], &IOVEC_MAKE_STRING("bb"))); + ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &IOVEC_MAKE_STRING("ccc"))); + + ASSERT_FALSE(iovec_inc_many(iovw.iovec, iovw.count, 4)); + ASSERT_NULL(iovw.iovec[0].iov_base); + ASSERT_EQ(iovw.iovec[0].iov_len, 0u); + ASSERT_NULL(iovw.iovec[1].iov_base); + ASSERT_EQ(iovw.iovec[1].iov_len, 0u); + ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &IOVEC_MAKE_STRING("c"))); + + ASSERT_TRUE(iovec_inc_many(iovw.iovec, iovw.count, 1)); + ASSERT_NULL(iovw.iovec[0].iov_base); + ASSERT_EQ(iovw.iovec[0].iov_len, 0u); + ASSERT_NULL(iovw.iovec[1].iov_base); + ASSERT_EQ(iovw.iovec[1].iov_len, 0u); + ASSERT_NULL(iovw.iovec[2].iov_base); + ASSERT_EQ(iovw.iovec[2].iov_len, 0u); + + ASSERT_TRUE(iovec_inc_many(iovw.iovec, iovw.count, 0)); + ASSERT_NULL(iovw.iovec[0].iov_base); + ASSERT_EQ(iovw.iovec[0].iov_len, 0u); + ASSERT_NULL(iovw.iovec[1].iov_base); + ASSERT_EQ(iovw.iovec[1].iov_len, 0u); + ASSERT_NULL(iovw.iovec[2].iov_base); + ASSERT_EQ(iovw.iovec[2].iov_len, 0u); + + ASSERT_SIGNAL(iovec_inc_many(iovw.iovec, iovw.count, 1), SIGABRT); +} + TEST(iovec_memcmp) { struct iovec iov1 = CONST_IOVEC_MAKE_STRING("abcdef"), iov2 = IOVEC_MAKE_STRING("bcdefg"), empty = {};