Thread
Commits
-
Fix incorrect return value for pg_size_pretty(bigint)
- 0a80e88d902d 15.8 landed
- 6f6b0f193db4 16.4 landed
- 1e020258e53c 17.0 landed
- b181062aa572 18.0 landed
-
Fix overflow in pg_size_pretty
Joseph Koshakow <koshy44@gmail.com> — 2024-07-27T19:18:00Z
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_t6tw@mail.gmail.com Thanks, Joseph Koshakow
-
Re: Fix overflow in pg_size_pretty
Joseph Koshakow <koshy44@gmail.com> — 2024-07-27T21:16:38Z
On Sat, Jul 27, 2024 at 3:18 PM Joseph Koshakow <koshy44@gmail.com> wrote: > > `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. Turns out it was just an order of operations issue. Fix is attached. Thanks, Joseph Koshakow
-
Re: Fix overflow in pg_size_pretty
David Rowley <dgrowleyml@gmail.com> — 2024-07-27T22:28:23Z
On Sun, 28 Jul 2024 at 07:18, Joseph Koshakow <koshy44@gmail.com> wrote: > 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. Could we just fix this more simply by assigning the absolute value of the signed variable into an unsigned type? It's a bit less code and gets rid of the explicit test for PG_INT64_MIN. David
-
Re: Fix overflow in pg_size_pretty
Joseph Koshakow <koshy44@gmail.com> — 2024-07-27T23:06:30Z
On Sat, Jul 27, 2024 at 6:28 PM David Rowley <dgrowleyml@gmail.com> wrote: > > On Sun, 28 Jul 2024 at 07:18, Joseph Koshakow <koshy44@gmail.com> wrote: >> 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. > > Could we just fix this more simply by assigning the absolute value of > the signed variable into an unsigned type? I might be misunderstanding, but my previous patch does assign the absolute value of the signed variable into an unsigned type. > It's a bit less code and > gets rid of the explicit test for PG_INT64_MIN. > + uint64 usize = size < 0 ? (uint64) (-size) : (uint64) size; I think that the explicit test for PG_INT64_MIN is still required. If `size` is equal to PG_INT64_MIN then `-size` will overflow. You end up with the correct behavior if `size` wraps around, but that's only guaranteed on platforms that support the `-fwrapv` flag. Thanks, Joseph Koshakow
-
Re: Fix overflow in pg_size_pretty
David Rowley <dgrowleyml@gmail.com> — 2024-07-28T00:00:21Z
On Sun, 28 Jul 2024 at 11:06, Joseph Koshakow <koshy44@gmail.com> wrote: > > + uint64 usize = size < 0 ? (uint64) (-size) : (uint64) size; > > I think that the explicit test for PG_INT64_MIN is still required. If > `size` is equal to PG_INT64_MIN then `-size` will overflow. You end up > with the correct behavior if `size` wraps around, but that's only > guaranteed on platforms that support the `-fwrapv` flag. What if we spelt it out the same way as pg_lltoa() does? i.e: uint64 usize = size < 0 ? 0 - (uint64) size : (uint64) size; David
-
Re: Fix overflow in pg_size_pretty
Joseph Koshakow <koshy44@gmail.com> — 2024-07-28T01:10:23Z
On Sat, Jul 27, 2024 at 8:00 PM David Rowley <dgrowleyml@gmail.com> wrote: > > On Sun, 28 Jul 2024 at 11:06, Joseph Koshakow <koshy44@gmail.com> wrote: >> > + uint64 usize = size < 0 ? (uint64) (-size) : (uint64) size; >> >> I think that the explicit test for PG_INT64_MIN is still required. If >> `size` is equal to PG_INT64_MIN then `-size` will overflow. You end up >> with the correct behavior if `size` wraps around, but that's only >> guaranteed on platforms that support the `-fwrapv` flag. > > What if we spelt it out the same way as pg_lltoa() does? > > i.e: uint64 usize = size < 0 ? 0 - (uint64) size : (uint64) size; My understanding of pg_lltoa() is that it produces an underflow and relies wrapping around from 0 to PG_UINT64_MAX. In fact the following SQL, which relies on pg_lltoa() under the hood, panics with `-ftrapv` enabled (which panics on underflows and overflows): SELECT int8out(-9223372036854775808); So we should actually probably modify pg_lltoa() to use pg_abs_s64() too. Thanks, Joe Koshakow -
Re: Fix overflow in pg_size_pretty
David Rowley <dgrowleyml@gmail.com> — 2024-07-28T03:42:36Z
On Sun, 28 Jul 2024 at 13:10, Joseph Koshakow <koshy44@gmail.com> wrote: > > On Sat, Jul 27, 2024 at 8:00 PM David Rowley <dgrowleyml@gmail.com> wrote: > > What if we spelt it out the same way as pg_lltoa() does? > > > > i.e: uint64 usize = size < 0 ? 0 - (uint64) size : (uint64) size; > > My understanding of pg_lltoa() is that it produces an underflow and > relies wrapping around from 0 to PG_UINT64_MAX. In fact the following > SQL, which relies on pg_lltoa() under the hood, panics with `-ftrapv` > enabled (which panics on underflows and overflows): > > SELECT int8out(-9223372036854775808); I didn't test to see where that's coming from, but I did test the two attached .c files. int.c uses the 0 - (unsigned int) var method and int2.c uses (unsigned int) (-var). Using clang and -ftrapv, I get: $ clang int.c -o int -O2 -ftrapv $ ./int 2147483648 $ clang int2.c -o int2 -O2 -ftrapv $ ./int2 Illegal instruction Similar with gcc: $ gcc int.c -o int -O2 -ftrapv $ ./int 2147483648 $ gcc int2.c -o int2 -O2 -ftrapv $ ./int2 Aborted I suspect your trap must be coming from somewhere else. It looks to me like the "uint64 usize = size < 0 ? 0 - (uint64) size : (uint64) size;" will be fine. David
-
Re: Fix overflow in pg_size_pretty
Joseph Koshakow <koshy44@gmail.com> — 2024-07-28T04:30:10Z
On Sat, Jul 27, 2024 at 11:42 PM David Rowley <dgrowleyml@gmail.com> wrote: > > I didn't test to see where that's coming from, but I did test the two > attached .c files. int.c uses the 0 - (unsigned int) var method and > int2.c uses (unsigned int) (-var). Using clang and -ftrapv, I get: > > $ clang int.c -o int -O2 -ftrapv > $ ./int > 2147483648 > $ clang int2.c -o int2 -O2 -ftrapv > $ ./int2 > Illegal instruction > > Similar with gcc: > $ gcc int.c -o int -O2 -ftrapv > $ ./int > 2147483648 > $ gcc int2.c -o int2 -O2 -ftrapv > $ ./int2 > Aborted > > I suspect your trap must be coming from somewhere else. It looks to me > like the "uint64 usize = size < 0 ? 0 - (uint64) size : (uint64) > size;" will be fine. My mistake, you're absolutely right. The trap is coming from `pg_strtoint64_safe()`. return -((int64) tmp); Which I had already addressed in the other thread and completely forgot about. I did some more research and it looks like unsigned integer arithmetic is guaranteed to wrap around, unlike signed integer arithmetic [0]. Attached is an updated patch with your approach. I removed the 0 from the negative case because I think it was unnecessary, but happy to add it back in if I missed something. Thanks for the review! Thanks, Joseph Koshakow [0] https://www.gnu.org/software/autoconf/manual/autoconf-2.63/html_node/Integer-Overflow-Basics.html -
Re: Fix overflow in pg_size_pretty
David Rowley <dgrowleyml@gmail.com> — 2024-07-28T10:34:49Z
On Sun, 28 Jul 2024 at 16:30, Joseph Koshakow <koshy44@gmail.com> wrote: > Attached is an updated patch with your approach. I removed the 0 from > the negative case because I think it was unnecessary, but happy to add > it back in if I missed something. I made a few adjustments and pushed this. I did keep the 0 - part as some compilers don't seem to like not having the 0. e.g MSVC gives: ../src/backend/utils/adt/dbsize.c(578): warning C4146: unary minus operator applied to unsigned type, result still unsigned I thought a bit about if it was really worth keeping the regression test or not and in the end decided it was likely worthwhile keeping it, so I expanded it slightly to cover both PG_INT64_MIN and PG_INT64_MAX values. It looks slightly less like we're earmarking the fact that there was a bug that way, and also seems to be of some additional value. PG15 did see quite a significant rewrite of the pg_size_pretty code. The bug does still exist in PG14 and earlier, but on looking at what it would take to fix it there I got a bit unexcited at the risk to reward ratio of adjusting that code and just left it alone. I've backpatched only as far as PG15. I'm sure someone else will feel I should have done something else there, but that's the judgement call I made. Thanks for the patch. David