From 7cc95fddc5b3d6124c5a340a0fb85d1dbf297d5d Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Tue, 23 Jun 2026 23:19:25 +0000 Subject: [PATCH v1] Protect against timestamp underflow in uuidv7(interval) We were silently allowing underflowing the generated timestamp for the UUIDv7 value: # SELECT uuid_extract_timestamp(uuidv7('-57 years'::interval)); uuid_extract_timestamp ----------------------------- 10889-01-23 04:02:36.375+00 (1 row) RFC 9562[0] doesn't seem to specify that this should be allowed, so generate an error and forbid the underflow. Link: https://www.rfc-editor.org/rfc/rfc9562.html [0] Signed-off-by: Tristan Partin --- src/backend/utils/adt/uuid.c | 5 +++++ src/test/regress/expected/uuid.out | 5 +++++ src/test/regress/sql/uuid.sql | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c index 6ee3752ac7..0c6ff59c06 100644 --- a/src/backend/utils/adt/uuid.c +++ b/src/backend/utils/adt/uuid.c @@ -689,6 +689,11 @@ uuidv7_interval(PG_FUNCTION_ARGS) /* Convert a TimestampTz value back to an UNIX epoch timestamp */ us = ts + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY * USECS_PER_SEC; + if (us < 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range for UUID version 7"), + errdetail("UUID version 7 does not support timestamps before the Unix epoch (1970-01-01 00:00:00 UTC)."))); /* Generate an UUIDv7 */ uuid = generate_uuidv7(us / US_PER_MS, (us % US_PER_MS) * NS_PER_US + ns % NS_PER_US); diff --git a/src/test/regress/expected/uuid.out b/src/test/regress/expected/uuid.out index 9c5dda9e9a..3750bc4814 100644 --- a/src/test/regress/expected/uuid.out +++ b/src/test/regress/expected/uuid.out @@ -261,6 +261,11 @@ SELECT y, ts, prev_ts FROM uuidts WHERE ts < prev_ts; ---+----+--------- (0 rows) +-- Check that we don't underflow when an interval pointing to before the Unix +-- epoch is used to create a UUIDv7 value +SELECT uuidv7('1969-12-31 23:59:59+00'::timestamptz - (now() AT TIME ZONE 'UTC')); +ERROR: timestamp out of range for UUID version 7 +DETAIL: UUID version 7 does not support timestamps before the Unix epoch (1970-01-01 00:00:00 UTC). -- extract functions -- version SELECT uuid_extract_version('11111111-1111-5111-8111-111111111111'); -- 5 diff --git a/src/test/regress/sql/uuid.sql b/src/test/regress/sql/uuid.sql index 8cc2ad4061..b820f0f170 100644 --- a/src/test/regress/sql/uuid.sql +++ b/src/test/regress/sql/uuid.sql @@ -140,6 +140,10 @@ WITH uuidts AS ( ) SELECT y, ts, prev_ts FROM uuidts WHERE ts < prev_ts; +-- Check that we don't underflow when an interval pointing to before the Unix +-- epoch is used to create a UUIDv7 value +SELECT uuidv7('1969-12-31 23:59:59+00'::timestamptz - (now() AT TIME ZONE 'UTC')); + -- extract functions -- version -- Tristan Partin https://tristan.partin.io