git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 4/5] git-compat-util: add `signed_mult_overflows` and `mult_overflows` macros

From
Johannes Schindelin via GitGitGadget <gitgitgadget@gmail.com>
Date
Oct 14, 2026, 06:09 UTC
Message-ID
<3dcc3d97570ea7f96628d02941d7424f84f7bf39.1791958171.git.gitgitgadget@gmail.com>
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
Previous: Phillip WoodNext: Phillip Wood
Message 11 of 16 in “Address overflows pointed out by Coverity”
  1. 0/5 Address overflows pointed out by CoverityJohannes Schindelin via GitGitGadget, Oct 14, 2026
  2. 1/5 archive-tar: guard get_path_prefix against zero-length inputJohannes Schindelin via GitGitGadget, Oct 14, 2026
  3. 2/5 http: use `size_t` for byte position in fwrite_sha1_fileJohannes Schindelin via GitGitGadget, Oct 14, 2026
  4. Patrick SteinhardtOct 16, 2026
  5. Junio C HamanoOct 16, 2026
  6. Simon RichterOct 17, 2026
  7. Junio C HamanoOct 17, 2026
  8. Junio C HamanoOct 17, 2026
  9. 3/5 wrapper: reject insanely large byte counts when reading/writingJohannes Schindelin via GitGitGadget, Oct 14, 2026
  10. Phillip WoodOct 14, 2026
  11. 4/5 git-compat-util: add `signed_mult_overflows` and `mult_overflows` macrosJohannes Schindelin via GitGitGadget, Oct 14, 2026
  12. Phillip WoodOct 15, 2026
  13. 5/5 date: avoid overflow in approxidate relative-time multiplicationJohannes Schindelin via GitGitGadget, Oct 14, 2026
  14. Junio C HamanoOct 14, 2026
  15. Phillip WoodOct 15, 2026
  16. Harald NordgrenOct 16, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.