postgres.git / summary / log / commit / refs
commit fec40878c8702ff4e58241d00a1914d8568bec03
Author: David Rowley <drowley@postgresql.org>
Date: Tue Aug 04 06:01:55 2026 +0000
Fix missing money overflow checks for INT64_MIN / -1
Similar to what 1f7cb5c30 did for the INT types, protect against
overflow when dividing the lowest possible money value by -1. This
cannot be represented on a two's complement machine.
Without this check, the result depends on the machine, and in the worst
case, could result in a crash. With the fix installed, this will now
result in:
ERROR: money out of range
Bug: #19585
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reported-by: Michael Malis <malis@pgrust.com>
Reviewed-by: Tristan Partin <tristan@partin.io>
Reviewed-by: Rafia Sabih <rafia.pghackers@gmail.com>
Discussion: https://postgr.es/m/19586-bb603bf5ad9934dd%40postgresql.org
Discussion: https://postgr.es/m/CAB8bMisnXJVXte6s3kUOpuuAY9%3D9kehG6MMX-%2BTQoFsSGan22Q%40mail.gmail.com
Backpatch-through: 14
src/backend/utils/adt/cash.c | 16 ++++++++++++++++
src/test/regress/expected/money.out | 8 ++++++++
src/test/regress/sql/money.sql | 4 ++++
3 files changed, 28 insertions(+)
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index 387bef50d62..29846610309 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -161,6 +161,22 @@ cash_div_int64(Cash c, int64 i)
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
+ /*
+ * INT64_MIN / -1 is problematic, since the result can't be represented on
+ * a two's-complement machine. Some machines produce INT64_MIN, some
+ * produce zero, some throw an exception. We can dodge the problem by
+ * recognizing that division by -1 is the same as negation.
+ */
+ if (i == -1)
+ {
+ if (unlikely(c == PG_INT64_MIN))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("money out of range")));
+ return -c;
+ }
+
+ /* No overflow is possible */
return c / i;
}
diff --git a/src/test/regress/expected/money.out b/src/test/regress/expected/money.out
index 90140f66767..7cebe8d2efb 100644
--- a/src/test/regress/expected/money.out
+++ b/src/test/regress/expected/money.out
@@ -514,6 +514,14 @@ SELECT '-1'::money / 1.175494e-38::float4;
ERROR: money out of range
SELECT '92233720368547758.07'::money * 2::int4;
ERROR: money out of range
+SELECT '-92233720368547758.08'::money * -1::int8;
+ERROR: money out of range
+SELECT '-92233720368547758.08'::money / -1::int8;
+ERROR: money out of range
+SELECT '-92233720368547758.08'::money / -1::int4;
+ERROR: money out of range
+SELECT '-92233720368547758.08'::money / -1::int2;
+ERROR: money out of range
SELECT '1'::money / 0::int2;
ERROR: division by zero
SELECT '42'::money * 'inf'::float8;
diff --git a/src/test/regress/sql/money.sql b/src/test/regress/sql/money.sql
index 1eb471d1e4e..6f8f7263a59 100644
--- a/src/test/regress/sql/money.sql
+++ b/src/test/regress/sql/money.sql
@@ -136,6 +136,10 @@ SELECT '-92233720368547758.08'::money - '0.01'::money;
SELECT '92233720368547758.07'::money * 2::float8;
SELECT '-1'::money / 1.175494e-38::float4;
SELECT '92233720368547758.07'::money * 2::int4;
+SELECT '-92233720368547758.08'::money * -1::int8;
+SELECT '-92233720368547758.08'::money / -1::int8;
+SELECT '-92233720368547758.08'::money / -1::int4;
+SELECT '-92233720368547758.08'::money / -1::int2;
SELECT '1'::money / 0::int2;
SELECT '42'::money * 'inf'::float8;
SELECT '42'::money * '-inf'::float8;
[parent: 529fc19ccc9c]