]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix missing money overflow checks for INT64_MIN / -1
authorDavid Rowley <drowley@postgresql.org>
Tue, 4 Aug 2026 05:59:13 +0000 (17:59 +1200)
committerDavid Rowley <drowley@postgresql.org>
Tue, 4 Aug 2026 05:59:13 +0000 (17:59 +1200)
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
src/test/regress/expected/money.out
src/test/regress/sql/money.sql

index 4bf60085c610e8c4c2c543cad29dc22d1e5fd5f1..310b3bb3fca6e05bffbc590eb13159289d5e4745 100644 (file)
@@ -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;
 }
 
index cc2ff4d96e80ff91b59b8e1693ed1f1dd0580734..e7fdb9b7d0d8140448326abbb15b29e07c8460ec 100644 (file)
@@ -539,6 +539,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;
index b888ec21c30c7022356fdb566e6a45b2f83dd655..d769a21109047b865d10f7ac5a0054bc49453145 100644 (file)
@@ -142,6 +142,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;