From: Dean Rasheed <dean.a.rasheed@gmail.com>
Date: Thu, 9 Nov 2023 09:55:39 +0000 (+0000)
Subject: Fix corner-case 64-bit integer subtraction bug on some platforms.
X-Git-Tag: REL_14_11~90
X-Git-Url: http://git.postgresql.org/gitweb/?a=commitdiff_plain;h=dea12b40d5210332453f4350e1653048b43fdb7a;p=postgresql.git

Fix corner-case 64-bit integer subtraction bug on some platforms.

When computing "0 - INT64_MIN", most platforms would report an
overflow error, which is correct. However, platforms without integer
overflow builtins or 128-bit integers would fail to spot the overflow,
and incorrectly return INT64_MIN.

Back-patch to all supported branches.

Patch be me. Thanks to Jian He for initial investigation, and Laurenz
Albe and Tom Lane for review.

Discussion: https://postgr.es/m/CAEZATCUNK-AZSD0jVdgkk0N%3DNcAXBWeAEX-QU9AnJPensikmdQ%40mail.gmail.com
---

diff --git a/src/include/common/int.h b/src/include/common/int.h
index 079954d7f0b..5ba9dd88e72 100644
--- a/src/include/common/int.h
+++ b/src/include/common/int.h
@@ -200,8 +200,12 @@ pg_sub_s64_overflow(int64 a, int64 b, int64 *result)
 	*result = (int64) res;
 	return false;
 #else
+	/*
+	 * Note: overflow is also possible when a == 0 and b < 0 (specifically,
+	 * when b == PG_INT64_MIN).
+	 */
 	if ((a < 0 && b > 0 && a < PG_INT64_MIN + b) ||
-		(a > 0 && b < 0 && a > PG_INT64_MAX + b))
+		(a >= 0 && b < 0 && a > PG_INT64_MAX + b))
 	{
 		*result = 0x5EED;		/* to avoid spurious warnings */
 		return true;
diff --git a/src/test/regress/expected/int8.out b/src/test/regress/expected/int8.out
index 36540ec4563..346fc172e72 100644
--- a/src/test/regress/expected/int8.out
+++ b/src/test/regress/expected/int8.out
@@ -659,6 +659,8 @@ select -('-9223372036854775807'::int8);
 
 select -('-9223372036854775808'::int8);
 ERROR:  bigint out of range
+select 0::int8 - '-9223372036854775808'::int8;
+ERROR:  bigint out of range
 select '9223372036854775800'::int8 + '9223372036854775800'::int8;
 ERROR:  bigint out of range
 select '-9223372036854775800'::int8 + '-9223372036854775800'::int8;
diff --git a/src/test/regress/sql/int8.sql b/src/test/regress/sql/int8.sql
index 32940b4daa5..bf9c40933cc 100644
--- a/src/test/regress/sql/int8.sql
+++ b/src/test/regress/sql/int8.sql
@@ -131,6 +131,7 @@ select '9223372036854775808'::int8;
 
 select -('-9223372036854775807'::int8);
 select -('-9223372036854775808'::int8);
+select 0::int8 - '-9223372036854775808'::int8;
 
 select '9223372036854775800'::int8 + '9223372036854775800'::int8;
 select '-9223372036854775800'::int8 + '-9223372036854775800'::int8;