Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Protect against overflow of ltree.numlevel and lquery.numlevel.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 28 Mar 2020 21:09:52 +0000 (17:09 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 28 Mar 2020 21:09:52 +0000 (17:09 -0400)
These uint16 fields could be overflowed by excessively long input,
producing strange results.  Complain for invalid input.

Likewise check for out-of-range values of the repeat counts in lquery.
(We don't try too hard on that one, notably not bothering to detect
if atoi's result has overflowed.)

Also detect length overflow in ltree_concat.

In passing, be more consistent about whether "syntax error" messages
include the type name.  Also, clarify the documentation about what
the size limit is.

This has been broken for a long time, so back-patch to all supported
branches.

Nikita Glukhov, reviewed by Benjie Gillam and Tomas Vondra

Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com

contrib/ltree/expected/ltree.out
contrib/ltree/ltree.h
contrib/ltree/ltree_io.c
contrib/ltree/ltree_op.c
contrib/ltree/sql/ltree.sql
doc/src/sgml/ltree.sgml

index fef2d40f173752b420ef69a2bdb3ee628ccf4ed2..6aa1c136464befc9ef42e533a56b30d1b262a035 100644 (file)
@@ -449,6 +449,52 @@ SELECT nlevel('1.2.3.4');
       4
 (1 row)
 
+SELECT nlevel(('1' || repeat('.1', 65534))::ltree);
+ nlevel 
+--------
+  65535
+(1 row)
+
+SELECT nlevel(('1' || repeat('.1', 65535))::ltree);
+ERROR:  number of ltree levels (65536) exceeds the maximum allowed (65535)
+SELECT nlevel(('1' || repeat('.1', 65534))::ltree || '1');
+ERROR:  number of ltree levels (65536) exceeds the maximum allowed (65535)
+SELECT ('1' || repeat('.1', 65534))::lquery IS NULL;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT ('1' || repeat('.1', 65535))::lquery IS NULL;
+ERROR:  number of lquery levels (65536) exceeds the maximum allowed (65535)
+SELECT '*{65535}'::lquery;
+  lquery  
+----------
+ *{65535}
+(1 row)
+
+SELECT '*{65536}'::lquery;
+ERROR:  lquery syntax error
+LINE 1: SELECT '*{65536}'::lquery;
+               ^
+DETAIL:  Low limit (65536) exceeds the maximum allowed (65535).
+SELECT '*{,65534}'::lquery;
+  lquery   
+-----------
+ *{,65534}
+(1 row)
+
+SELECT '*{,65535}'::lquery;
+ lquery 
+--------
+ *
+(1 row)
+
+SELECT '*{,65536}'::lquery;
+ERROR:  lquery syntax error
+LINE 1: SELECT '*{,65536}'::lquery;
+               ^
+DETAIL:  High limit (65536) exceeds the maximum allowed (65535).
 SELECT '1.2'::ltree  < '2.2'::ltree;
  ?column? 
 ----------
index c604357dbfbb215ced194ed801fbe90247dd62d5..a8a3dc60f008a7511a30085933cb18a2c2584ddc 100644 (file)
@@ -25,6 +25,7 @@ typedef struct
 
 #define LTREE_HDRSIZE  MAXALIGN( offsetof(ltree, data) )
 #define LTREE_FIRST(x) ( (ltree_level*)( ((char*)(x))+LTREE_HDRSIZE ) )
+#define LTREE_MAX_LEVELS   PG_UINT16_MAX   /* ltree.numlevel is uint16 */
 
 
 /* lquery */
@@ -77,6 +78,7 @@ typedef struct
 
 #define LQUERY_HDRSIZE  MAXALIGN( offsetof(lquery, data) )
 #define LQUERY_FIRST(x)   ( (lquery_level*)( ((char*)(x))+LQUERY_HDRSIZE ) )
+#define LQUERY_MAX_LEVELS  PG_UINT16_MAX   /* lquery.numlevel is uint16 */
 
 #define LQUERY_HASNOT      0x01
 
index a1d4a0d38faf493072194fb5fdf98fd2565c1e58..2cd219a2302b4f6d8b051d5e5381fd88917484ae 100644 (file)
@@ -58,11 +58,11 @@ ltree_in(PG_FUNCTION_ARGS)
        ptr += charlen;
    }
 
-   if (num + 1 > MaxAllocSize / sizeof(nodeitem))
+   if (num + 1 > LTREE_MAX_LEVELS)
        ereport(ERROR,
                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-            errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
-                   num + 1, (int) (MaxAllocSize / sizeof(nodeitem)))));
+                errmsg("number of ltree levels (%d) exceeds the maximum allowed (%d)",
+                       num + 1, LTREE_MAX_LEVELS)));
    list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
    ptr = buf;
    while (*ptr)
@@ -227,11 +227,11 @@ lquery_in(PG_FUNCTION_ARGS)
    }
 
    num++;
-   if (num > MaxAllocSize / ITEMSIZE)
+   if (num > LQUERY_MAX_LEVELS)
        ereport(ERROR,
                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-            errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
-                   num, (int) (MaxAllocSize / ITEMSIZE))));
+                errmsg("number of lquery levels (%d) exceeds the maximum allowed (%d)",
+                       num, LQUERY_MAX_LEVELS)));
    curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
    ptr = buf;
    while (*ptr)
@@ -344,7 +344,7 @@ lquery_in(PG_FUNCTION_ARGS)
            else if (charlen == 1 && t_iseq(ptr, '.'))
            {
                curqlevel->low = 0;
-               curqlevel->high = 0xffff;
+               curqlevel->high = LTREE_MAX_LEVELS;
                curqlevel = NEXTLEV(curqlevel);
                state = LQPRS_WAITLEVEL;
            }
@@ -357,7 +357,16 @@ lquery_in(PG_FUNCTION_ARGS)
                state = LQPRS_WAITSNUM;
            else if (t_isdigit(ptr))
            {
-               curqlevel->low = atoi(ptr);
+               int         low = atoi(ptr);
+
+               if (low < 0 || low > LTREE_MAX_LEVELS)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_SYNTAX_ERROR),
+                            errmsg("lquery syntax error"),
+                            errdetail("Low limit (%d) exceeds the maximum allowed (%d).",
+                                      low, LTREE_MAX_LEVELS)));
+
+               curqlevel->low = (uint16) low;
                state = LQPRS_WAITND;
            }
            else
@@ -367,12 +376,21 @@ lquery_in(PG_FUNCTION_ARGS)
        {
            if (t_isdigit(ptr))
            {
-               curqlevel->high = atoi(ptr);
+               int         high = atoi(ptr);
+
+               if (high < 0 || high > LTREE_MAX_LEVELS)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_SYNTAX_ERROR),
+                            errmsg("lquery syntax error"),
+                            errdetail("High limit (%d) exceeds the maximum allowed (%d).",
+                                      high, LTREE_MAX_LEVELS)));
+
+               curqlevel->high = (uint16) high;
                state = LQPRS_WAITCLOSE;
            }
            else if (charlen == 1 && t_iseq(ptr, '}'))
            {
-               curqlevel->high = 0xffff;
+               curqlevel->high = LTREE_MAX_LEVELS;
                state = LQPRS_WAITEND;
            }
            else
@@ -422,7 +440,7 @@ lquery_in(PG_FUNCTION_ARGS)
        if (lptr->start == ptr)
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
-                    errmsg("syntax error"),
+                    errmsg("lquery syntax error"),
                     errdetail("Unexpected end of line.")));
 
        lptr->len = ptr - lptr->start -
@@ -432,7 +450,7 @@ lquery_in(PG_FUNCTION_ARGS)
        if (lptr->len == 0)
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
-                    errmsg("syntax error"),
+                    errmsg("lquery syntax error"),
                     errdetail("Unexpected end of line.")));
 
        if (lptr->wlen > 255)
@@ -444,11 +462,11 @@ lquery_in(PG_FUNCTION_ARGS)
                               lptr->wlen, pos)));
    }
    else if (state == LQPRS_WAITOPEN)
-       curqlevel->high = 0xffff;
+       curqlevel->high = LTREE_MAX_LEVELS;
    else if (state != LQPRS_WAITEND)
        ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
-                errmsg("syntax error"),
+                errmsg("lquery syntax error"),
                 errdetail("Unexpected end of line.")));
 
    curqlevel = tmpql;
@@ -468,8 +486,8 @@ lquery_in(PG_FUNCTION_ARGS)
        else if (curqlevel->low > curqlevel->high)
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
-                    errmsg("syntax error"),
-                    errdetail("Low limit(%d) is greater than upper(%d).",
+                    errmsg("lquery syntax error"),
+                    errdetail("Low limit (%d) is greater than upper (%d).",
                               curqlevel->low, curqlevel->high)));
 
        curqlevel = NEXTLEV(curqlevel);
@@ -593,7 +611,7 @@ lquery_out(PG_FUNCTION_ARGS)
            }
            else if (curqlevel->low == 0)
            {
-               if (curqlevel->high == 0xffff)
+               if (curqlevel->high == LTREE_MAX_LEVELS)
                {
                    *ptr = '*';
                    *(ptr + 1) = '\0';
@@ -601,7 +619,7 @@ lquery_out(PG_FUNCTION_ARGS)
                else
                    sprintf(ptr, "*{,%d}", curqlevel->high);
            }
-           else if (curqlevel->high == 0xffff)
+           else if (curqlevel->high == LTREE_MAX_LEVELS)
            {
                sprintf(ptr, "*{%d,}", curqlevel->low);
            }
index d1067f8146567beee33990fa7f3b2ee85d3115de..6c7269cc8a2a36c6653e560dbe80acdb49e2e9de 100644 (file)
@@ -274,10 +274,17 @@ static ltree *
 ltree_concat(ltree *a, ltree *b)
 {
    ltree      *r;
+   int         numlevel = (int) a->numlevel + b->numlevel;
+
+   if (numlevel > LTREE_MAX_LEVELS)
+       ereport(ERROR,
+               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                errmsg("number of ltree levels (%d) exceeds the maximum allowed (%d)",
+                       numlevel, LTREE_MAX_LEVELS)));
 
    r = (ltree *) palloc0(VARSIZE(a) + VARSIZE(b) - LTREE_HDRSIZE);
    SET_VARSIZE(r, VARSIZE(a) + VARSIZE(b) - LTREE_HDRSIZE);
-   r->numlevel = a->numlevel + b->numlevel;
+   r->numlevel = (uint16) numlevel;
 
    memcpy(LTREE_FIRST(r), LTREE_FIRST(a), VARSIZE(a) - LTREE_HDRSIZE);
    memcpy(((char *) LTREE_FIRST(r)) + VARSIZE(a) - LTREE_HDRSIZE,
index 4dbc14d67a3f28668bb4b6ba25fcdf799e6de643..2107d831b61c59bb6a22a5a65e997de4e2a9bf03 100644 (file)
@@ -85,6 +85,17 @@ SELECT '1.*.4|3|2.*{1}'::lquery;
 SELECT 'qwerty%@*.tu'::lquery;
 
 SELECT nlevel('1.2.3.4');
+SELECT nlevel(('1' || repeat('.1', 65534))::ltree);
+SELECT nlevel(('1' || repeat('.1', 65535))::ltree);
+SELECT nlevel(('1' || repeat('.1', 65534))::ltree || '1');
+SELECT ('1' || repeat('.1', 65534))::lquery IS NULL;
+SELECT ('1' || repeat('.1', 65535))::lquery IS NULL;
+SELECT '*{65535}'::lquery;
+SELECT '*{65536}'::lquery;
+SELECT '*{,65534}'::lquery;
+SELECT '*{,65535}'::lquery;
+SELECT '*{,65536}'::lquery;
+
 SELECT '1.2'::ltree  < '2.2'::ltree;
 SELECT '1.2'::ltree  <= '2.2'::ltree;
 SELECT '2.2'::ltree  = '2.2'::ltree;
index 61b9ae0cc8d8b37c47356360319edfec936742f4..97dc8aa2d06abc13029904045c69ea848ce61636 100644 (file)
@@ -31,8 +31,7 @@
    A <firstterm>label path</firstterm> is a sequence of zero or more
    labels separated by dots, for example <literal>L1.L2.L3</>, representing
    a path from the root of a hierarchical tree to a particular node.  The
-   length of a label path must be less than 65Kb, but keeping it under 2Kb is
-   preferable.
+   length of a label path cannot exceed 65535 labels.
   </para>
 
   <para>