Tighten overflow checks in tidin().
authorTom Lane
Fri, 4 Mar 2022 01:03:47 +0000 (20:03 -0500)
committerTom Lane
Fri, 4 Mar 2022 01:04:35 +0000 (20:04 -0500)
This code seems to have been written on the assumption that
"unsigned long" is 32 bits; or at any rate it ignored the
possibility of conversion overflow.  Rewrite, borrowing some
logic from oidin().

Discussion: https://postgr.es/m/3441768.1646343914@sss.pgh.pa.us

src/backend/utils/adt/tid.c
src/test/regress/expected/tid.out
src/test/regress/sql/tid.sql

index dcc1620afbdbbfdf08bac2ee633494a6ce207b7d..83ac589f9579939fd46fb126097f35bdc6c80bd3 100644 (file)
@@ -64,10 +64,10 @@ tidin(PG_FUNCTION_ARGS)
    BlockNumber blockNumber;
    OffsetNumber offsetNumber;
    char       *badp;
-   int         hold_offset;
+   unsigned long cvt;
 
    for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
-       if (*p == DELIM || (*p == LDELIM && !i))
+       if (*p == DELIM || (*p == LDELIM && i == 0))
            coord[i++] = p + 1;
 
    if (i < NTIDARGS)
@@ -77,22 +77,36 @@ tidin(PG_FUNCTION_ARGS)
                        "tid", str)));
 
    errno = 0;
-   blockNumber = strtoul(coord[0], &badp, 10);
+   cvt = strtoul(coord[0], &badp, 10);
    if (errno || *badp != DELIM)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                 errmsg("invalid input syntax for type %s: \"%s\"",
                        "tid", str)));
+   blockNumber = (BlockNumber) cvt;
 
-   hold_offset = strtol(coord[1], &badp, 10);
-   if (errno || *badp != RDELIM ||
-       hold_offset > USHRT_MAX || hold_offset < 0)
+   /*
+    * Cope with possibility that unsigned long is wider than BlockNumber, in
+    * which case strtoul will not raise an error for some values that are out
+    * of the range of BlockNumber.  (See similar code in oidin().)
+    */
+#if SIZEOF_LONG > 4
+   if (cvt != (unsigned long) blockNumber &&
+       cvt != (unsigned long) ((int32) blockNumber))
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                 errmsg("invalid input syntax for type %s: \"%s\"",
                        "tid", str)));
+#endif
 
-   offsetNumber = hold_offset;
+   cvt = strtoul(coord[1], &badp, 10);
+   if (errno || *badp != RDELIM ||
+       cvt > USHRT_MAX)
+       ereport(ERROR,
+               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                errmsg("invalid input syntax for type %s: \"%s\"",
+                       "tid", str)));
+   offsetNumber = (OffsetNumber) cvt;
 
    result = (ItemPointer) palloc(sizeof(ItemPointerData));
 
index 8da1a455761170a437308673506ac5730d775dbd..7d8957bd6f742d483082d67f04bc43e0129bf351 100644 (file)
@@ -1,3 +1,22 @@
+-- basic tests for the TID data type
+SELECT
+  '(0,0)'::tid as tid00,
+  '(0,1)'::tid as tid01,
+  '(-1,0)'::tid as tidm10,
+  '(4294967295,65535)'::tid as tidmax;
+ tid00 | tid01 |     tidm10     |       tidmax       
+-------+-------+----------------+--------------------
+ (0,0) | (0,1) | (4294967295,0) | (4294967295,65535)
+(1 row)
+
+SELECT '(4294967296,1)'::tid;  -- error
+ERROR:  invalid input syntax for type tid: "(4294967296,1)"
+LINE 1: SELECT '(4294967296,1)'::tid;
+               ^
+SELECT '(1,65536)'::tid;  -- error
+ERROR:  invalid input syntax for type tid: "(1,65536)"
+LINE 1: SELECT '(1,65536)'::tid;
+               ^
 -- tests for functions related to TID handling
 CREATE TABLE tid_tab (a int);
 -- min() and max() for TIDs
index 34546a3cb7a3bc336d5c21248113c1164d5e5062..990d314a5f8d6baa664a3b81de72c351a9023581 100644 (file)
@@ -1,3 +1,15 @@
+-- basic tests for the TID data type
+
+SELECT
+  '(0,0)'::tid as tid00,
+  '(0,1)'::tid as tid01,
+  '(-1,0)'::tid as tidm10,
+  '(4294967295,65535)'::tid as tidmax;
+
+SELECT '(4294967296,1)'::tid;  -- error
+SELECT '(1,65536)'::tid;  -- error
+
+
 -- tests for functions related to TID handling
 
 CREATE TABLE tid_tab (a int);