Hi all, Attached is a patch that resolves an overflow in pg_size_pretty() that resulted in unexpected behavior when PG_INT64_MIN was passed in as an argument.
The pg_abs_s64() helper function is extracted and simplified from patch 0001 from [0]. I didn't add similar functions for other sized integers since they'd be unused, but I'd be happy to add them if others disagree. `SELECT -9223372036854775808::bigint` results in an out of range error, even though `-9223372036854775808` can fit in a `bigint` and `SELECT pg_typeof(-9223372036854775808)` returns `bigint`. That's why the `::bigint` cast is omitted from my test. [0] https://www.postgresql.org/message-id/flat/caavxfhdbpoyegs7s+xf4iaw0-cgiq25jpydwbqqqvltle_t...@mail.gmail.com Thanks, Joseph Koshakow
From 6ec885412f2e0f3a3e019ec1906901e39c6d517a Mon Sep 17 00:00:00 2001 From: Joseph Koshakow <kosh...@gmail.com> Date: Sat, 27 Jul 2024 15:06:09 -0400 Subject: [PATCH] Fix overflow in pg_size_pretty This commit removes an overflow from pg_size_pretty that causes PG_INT64_MIN to by displayed with the bytes unit instead of the PB unit. --- src/backend/utils/adt/dbsize.c | 3 ++- src/include/common/int.h | 13 +++++++++++++ src/test/regress/expected/dbsize.out | 6 ++++++ src/test/regress/sql/dbsize.sql | 2 ++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c index 25d7110c13..5648f40101 100644 --- a/src/backend/utils/adt/dbsize.c +++ b/src/backend/utils/adt/dbsize.c @@ -21,6 +21,7 @@ #include "catalog/pg_tablespace.h" #include "commands/dbcommands.h" #include "commands/tablespace.h" +#include "common/int.h" #include "miscadmin.h" #include "storage/fd.h" #include "utils/acl.h" @@ -577,7 +578,7 @@ pg_size_pretty(PG_FUNCTION_ARGS) uint8 bits; /* use this unit if there are no more units or we're below the limit */ - if (unit[1].name == NULL || i64abs(size) < unit->limit) + if (unit[1].name == NULL || pg_abs_s64(size) < unit->limit) { if (unit->round) size = half_rounded(size); diff --git a/src/include/common/int.h b/src/include/common/int.h index 7fc046e78a..d86eb6dd5e 100644 --- a/src/include/common/int.h +++ b/src/include/common/int.h @@ -258,6 +258,19 @@ pg_mul_s64_overflow(int64 a, int64 b, int64 *result) #endif } +static inline uint64 +pg_abs_s64(int64 a) +{ + if (unlikely(a == PG_INT64_MIN)) + { + return ((uint64) PG_INT64_MAX) + 1; + } + else + { + return (uint64) i64abs(a); + } +} + /*------------------------------------------------------------------------ * Overflow routines for unsigned integers *------------------------------------------------------------------------ diff --git a/src/test/regress/expected/dbsize.out b/src/test/regress/expected/dbsize.out index f1121a87aa..3398a3eceb 100644 --- a/src/test/regress/expected/dbsize.out +++ b/src/test/regress/expected/dbsize.out @@ -12,6 +12,12 @@ SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM 1000000000000000 | 909 TB | -909 TB (6 rows) +SELECT pg_size_pretty(-9223372036854775808); + pg_size_pretty +---------------- + -8192 PB +(1 row) + SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM (VALUES (10::numeric), (1000::numeric), (1000000::numeric), (1000000000::numeric), (1000000000000::numeric), diff --git a/src/test/regress/sql/dbsize.sql b/src/test/regress/sql/dbsize.sql index b34cf33385..e3e18e948e 100644 --- a/src/test/regress/sql/dbsize.sql +++ b/src/test/regress/sql/dbsize.sql @@ -3,6 +3,8 @@ SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM (1000000000::bigint), (1000000000000::bigint), (1000000000000000::bigint)) x(size); +SELECT pg_size_pretty(-9223372036854775808); + SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM (VALUES (10::numeric), (1000::numeric), (1000000::numeric), (1000000000::numeric), (1000000000000::numeric), -- 2.34.1