From: "Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Johannes Schindelin <johannes.schindelin@gmx.de>,
Johannes Schindelin <johannes.schindelin@gmx.de>
Subject: [PATCH 4/5] git-compat-util: add `signed_mult_overflows` and `mult_overflows` macros
Date: Wed, 14 Oct 2026 06:09:30 +0000 [thread overview]
Message-ID: <3dcc3d97570ea7f96628d02941d7424f84f7bf39.1791958171.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2253.git.1791958171.gitgitgadget@gmail.com>
From: Johannes Schindelin <johannes.schindelin@gmx.de>
`unsigned_mult_overflows()` does not cover signed arithmetic, but types
such as `time_t` can be signed or unsigned. Add signed and type-generic
guards so callers can check multiplication overflow in either case.
Overflow depends on the product type selected by C's usual arithmetic
conversions. Products equal to the signed minimum are representable and
must not be rejected.
Assisted-by: Claude Opus 4.6, GPT-6 and GPT-6.1
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
git-compat-util.h | 50 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/git-compat-util.h b/git-compat-util.h
index afce8b85b8..077fa94873 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -85,9 +85,27 @@ struct strbuf;
#define bitsizeof(x) (CHAR_BIT * sizeof(x))
+#if GIT_GNUC_PREREQ(3, 1)
+#define is_unsigned_type(a) ((__typeof__(a))-1 >= 0)
+#elif __STDC_VERSION__ >= 201112L
+#define is_unsigned_type(a) _Generic((a), \
+ unsigned char: 1, \
+ unsigned short: 1, \
+ unsigned int: 1, \
+ unsigned long: 1, \
+ unsigned long long: 1, \
+ _Bool: 1, \
+ char: (CHAR_MIN == 0), \
+ default: 0)
+#endif
+
#define maximum_signed_value_of_type(a) \
(INTMAX_MAX >> (bitsizeof(intmax_t) - bitsizeof(a)))
+/* Account for the extra negative value on two's-complement targets. */
+#define minimum_signed_value_of_type(a) \
+ (-maximum_signed_value_of_type(a) - (INTMAX_MIN < -INTMAX_MAX))
+
#define maximum_unsigned_value_of_type(a) \
(UINTMAX_MAX >> (bitsizeof(uintmax_t) - bitsizeof(a)))
@@ -111,6 +129,38 @@ struct strbuf;
#define unsigned_mult_overflows(a, b) \
((a) && (b) > maximum_unsigned_value_of_type(a) / (a))
+/*
+ * Returns true if the multiplication of "a" and "b" will
+ * overflow or underflow a signed type. The types of "a" and "b"
+ * must match and must be signed. Note that this macro evaluates
+ * both "a" and "b" twice!
+ */
+#define signed_mult_overflows(a, b) \
+ (((a) > 0 && (b) > 0 && (a) > maximum_signed_value_of_type(a) / (b)) || \
+ ((a) < 0 && (b) < 0 && (a) < maximum_signed_value_of_type(a) / (b)) || \
+ ((a) > 0 && (b) < 0 && (b) < minimum_signed_value_of_type(a) / (a)) || \
+ ((a) < 0 && (b) > 0 && (a) < minimum_signed_value_of_type(b) / (b)))
+
+/*
+ * Returns true if the product of "a" and "b", after the usual arithmetic
+ * conversions, cannot be represented in the multiplication expression's
+ * type. The operands must be integers and may be evaluated more than once.
+ */
+#if GIT_GNUC_PREREQ(3, 1)
+#define mult_overflows(a, b) \
+ (is_unsigned_type((a) * (b)) \
+ ? unsigned_mult_overflows((__typeof__((a) * (b)))(a), \
+ (__typeof__((a) * (b)))(b)) \
+ : signed_mult_overflows((__typeof__((a) * (b)))(a), \
+ (__typeof__((a) * (b)))(b)))
+#else
+/*
+ * This implementation needs GNU `typeof` to convert both operands to the
+ * multiplication's type. Skip the check without that extension.
+ */
+#define mult_overflows(a, b) 0
+#endif
+
/*
* Returns true if the left shift of "a" by "shift" bits will
* overflow. The type of "a" must be unsigned.
--
gitgitgadget
next prev parent reply other threads:[~2026-10-14 6:09 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-14 6:09 [PATCH 0/5] Address overflows pointed out by Coverity Johannes Schindelin via GitGitGadget
2026-10-14 6:09 ` [PATCH 1/5] archive-tar: guard get_path_prefix against zero-length input Johannes Schindelin via GitGitGadget
2026-10-14 6:09 ` [PATCH 2/5] http: use `size_t` for byte position in fwrite_sha1_file Johannes Schindelin via GitGitGadget
2026-10-14 6:09 ` [PATCH 3/5] wrapper: reject insanely large byte counts when reading/writing Johannes Schindelin via GitGitGadget
2026-10-14 11:37 ` Phillip Wood
2026-10-14 6:09 ` Johannes Schindelin via GitGitGadget [this message]
2026-10-14 6:09 ` [PATCH 5/5] date: avoid overflow in approxidate relative-time multiplication Johannes Schindelin via GitGitGadget
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3dcc3d97570ea7f96628d02941d7424f84f7bf39.1791958171.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=johannes.schindelin@gmx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox