]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
macro: introduce u64_multiply_safe() to avoid overflow
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 18 Oct 2023 09:10:50 +0000 (18:10 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 19 Oct 2023 09:31:44 +0000 (18:31 +0900)
Just a paranoia.

src/basic/macro.h
src/libsystemd/sd-journal/journal-file.c
src/test/test-macro.c

index dcd1ca96fd48c58e409d7daaec8150fb305f0060..d3eb980abd3a0f5b7861e0e4b056057afe53c3ac 100644 (file)
 #error "neither int nor long are four bytes long?!?"
 #endif
 
+static inline uint64_t u64_multiply_safe(uint64_t a, uint64_t b) {
+        if (_unlikely_(a != 0 && b > (UINT64_MAX / a)))
+                return 0; /* overflow */
+
+        return a * b;
+}
+
 /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
 static inline unsigned long ALIGN_POWER2(unsigned long u) {
 
index 7af439c2e73dd50e366bf8937e01b91167e244ec..d138e3809638d84cd951d5d372808c184ada0278 100644 (file)
@@ -795,7 +795,7 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size)
                 if (fstatvfs(f->fd, &svfs) >= 0) {
                         uint64_t available;
 
-                        available = LESS_BY((uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize, f->metrics.keep_free);
+                        available = LESS_BY(u64_multiply_safe(svfs.f_bfree, svfs.f_bsize), f->metrics.keep_free);
 
                         if (new_size - old_size > available)
                                 return -E2BIG;
@@ -3881,7 +3881,7 @@ static void journal_default_metrics(JournalMetrics *m, int fd, bool compact) {
         assert(fd >= 0);
 
         if (fstatvfs(fd, &ss) >= 0)
-                fs_size = ss.f_frsize * ss.f_blocks;
+                fs_size = u64_multiply_safe(ss.f_frsize, ss.f_blocks);
         else
                 log_debug_errno(errno, "Failed to determine disk size: %m");
 
index 1b5ef9718e089ec047a1040f483e7fecd2f3dbef..63fe6ea95e76bdb7fdb6505338150453a660a06e 100644 (file)
@@ -1012,4 +1012,29 @@ TEST(round_up) {
         TEST_ROUND_UP_BY_TYPE(uint64_t, UINT64_MAX);
 }
 
+TEST(u64_multiply_safe) {
+        assert_se(u64_multiply_safe(0, 0) == 0);
+        assert_se(u64_multiply_safe(10, 0) == 0);
+        assert_se(u64_multiply_safe(0, 10) == 0);
+        assert_se(u64_multiply_safe(10, 10) == 100);
+
+        assert_se(u64_multiply_safe(UINT64_MAX, 0) == 0);
+        assert_se(u64_multiply_safe(UINT64_MAX, 1) == UINT64_MAX);
+        assert_se(u64_multiply_safe(UINT64_MAX, 2) == 0);
+        assert_se(u64_multiply_safe(0, UINT64_MAX) == 0);
+        assert_se(u64_multiply_safe(1, UINT64_MAX) == UINT64_MAX);
+        assert_se(u64_multiply_safe(2, UINT64_MAX) == 0);
+
+        assert_se(u64_multiply_safe(UINT64_MAX / 2, 0) == 0);
+        assert_se(u64_multiply_safe(UINT64_MAX / 2, 1) == UINT64_MAX / 2);
+        assert_se(u64_multiply_safe(UINT64_MAX / 2, 2) == UINT64_MAX - 1);
+        assert_se(u64_multiply_safe(UINT64_MAX / 2, 3) == 0);
+        assert_se(u64_multiply_safe(0, UINT64_MAX / 2) == 0);
+        assert_se(u64_multiply_safe(1, UINT64_MAX / 2) == UINT64_MAX / 2);
+        assert_se(u64_multiply_safe(2, UINT64_MAX / 2) == UINT64_MAX - 1);
+        assert_se(u64_multiply_safe(3, UINT64_MAX / 2) == 0);
+
+        assert_se(u64_multiply_safe(UINT64_MAX, UINT64_MAX) == 0);
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);