Commit 95f7ddfd authored by Tom Lane's avatar Tom Lane

Protect against overflow of ltree.numlevel and lquery.numlevel.

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
parent 42750b08
...@@ -457,6 +457,52 @@ SELECT nlevel('1.2.3.4'); ...@@ -457,6 +457,52 @@ SELECT nlevel('1.2.3.4');
4 4
(1 row) (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; SELECT '1.2'::ltree < '2.2'::ltree;
?column? ?column?
---------- ----------
......
...@@ -25,6 +25,7 @@ typedef struct ...@@ -25,6 +25,7 @@ typedef struct
#define LTREE_HDRSIZE MAXALIGN( offsetof(ltree, data) ) #define LTREE_HDRSIZE MAXALIGN( offsetof(ltree, data) )
#define LTREE_FIRST(x) ( (ltree_level*)( ((char*)(x))+LTREE_HDRSIZE ) ) #define LTREE_FIRST(x) ( (ltree_level*)( ((char*)(x))+LTREE_HDRSIZE ) )
#define LTREE_MAX_LEVELS PG_UINT16_MAX /* ltree.numlevel is uint16 */
/* lquery */ /* lquery */
...@@ -77,6 +78,7 @@ typedef struct ...@@ -77,6 +78,7 @@ typedef struct
#define LQUERY_HDRSIZE MAXALIGN( offsetof(lquery, data) ) #define LQUERY_HDRSIZE MAXALIGN( offsetof(lquery, data) )
#define LQUERY_FIRST(x) ( (lquery_level*)( ((char*)(x))+LQUERY_HDRSIZE ) ) #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 #define LQUERY_HASNOT 0x01
......
...@@ -58,11 +58,11 @@ ltree_in(PG_FUNCTION_ARGS) ...@@ -58,11 +58,11 @@ ltree_in(PG_FUNCTION_ARGS)
ptr += charlen; ptr += charlen;
} }
if (num + 1 > MaxAllocSize / sizeof(nodeitem)) if (num + 1 > LTREE_MAX_LEVELS)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of levels (%d) exceeds the maximum allowed (%d)", errmsg("number of ltree levels (%d) exceeds the maximum allowed (%d)",
num + 1, (int) (MaxAllocSize / sizeof(nodeitem))))); num + 1, LTREE_MAX_LEVELS)));
list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1)); list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
ptr = buf; ptr = buf;
while (*ptr) while (*ptr)
...@@ -227,11 +227,11 @@ lquery_in(PG_FUNCTION_ARGS) ...@@ -227,11 +227,11 @@ lquery_in(PG_FUNCTION_ARGS)
} }
num++; num++;
if (num > MaxAllocSize / ITEMSIZE) if (num > LQUERY_MAX_LEVELS)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of levels (%d) exceeds the maximum allowed (%d)", errmsg("number of lquery levels (%d) exceeds the maximum allowed (%d)",
num, (int) (MaxAllocSize / ITEMSIZE)))); num, LQUERY_MAX_LEVELS)));
curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num); curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
ptr = buf; ptr = buf;
while (*ptr) while (*ptr)
...@@ -344,7 +344,7 @@ lquery_in(PG_FUNCTION_ARGS) ...@@ -344,7 +344,7 @@ lquery_in(PG_FUNCTION_ARGS)
else if (charlen == 1 && t_iseq(ptr, '.')) else if (charlen == 1 && t_iseq(ptr, '.'))
{ {
curqlevel->low = 0; curqlevel->low = 0;
curqlevel->high = 0xffff; curqlevel->high = LTREE_MAX_LEVELS;
curqlevel = NEXTLEV(curqlevel); curqlevel = NEXTLEV(curqlevel);
state = LQPRS_WAITLEVEL; state = LQPRS_WAITLEVEL;
} }
...@@ -357,7 +357,16 @@ lquery_in(PG_FUNCTION_ARGS) ...@@ -357,7 +357,16 @@ lquery_in(PG_FUNCTION_ARGS)
state = LQPRS_WAITSNUM; state = LQPRS_WAITSNUM;
else if (t_isdigit(ptr)) 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; state = LQPRS_WAITND;
} }
else else
...@@ -367,12 +376,21 @@ lquery_in(PG_FUNCTION_ARGS) ...@@ -367,12 +376,21 @@ lquery_in(PG_FUNCTION_ARGS)
{ {
if (t_isdigit(ptr)) 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; state = LQPRS_WAITCLOSE;
} }
else if (charlen == 1 && t_iseq(ptr, '}')) else if (charlen == 1 && t_iseq(ptr, '}'))
{ {
curqlevel->high = 0xffff; curqlevel->high = LTREE_MAX_LEVELS;
state = LQPRS_WAITEND; state = LQPRS_WAITEND;
} }
else else
...@@ -422,7 +440,7 @@ lquery_in(PG_FUNCTION_ARGS) ...@@ -422,7 +440,7 @@ lquery_in(PG_FUNCTION_ARGS)
if (lptr->start == ptr) if (lptr->start == ptr)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error"), errmsg("lquery syntax error"),
errdetail("Unexpected end of line."))); errdetail("Unexpected end of line.")));
lptr->len = ptr - lptr->start - lptr->len = ptr - lptr->start -
...@@ -432,7 +450,7 @@ lquery_in(PG_FUNCTION_ARGS) ...@@ -432,7 +450,7 @@ lquery_in(PG_FUNCTION_ARGS)
if (lptr->len == 0) if (lptr->len == 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error"), errmsg("lquery syntax error"),
errdetail("Unexpected end of line."))); errdetail("Unexpected end of line.")));
if (lptr->wlen > 255) if (lptr->wlen > 255)
...@@ -444,11 +462,11 @@ lquery_in(PG_FUNCTION_ARGS) ...@@ -444,11 +462,11 @@ lquery_in(PG_FUNCTION_ARGS)
lptr->wlen, pos))); lptr->wlen, pos)));
} }
else if (state == LQPRS_WAITOPEN) else if (state == LQPRS_WAITOPEN)
curqlevel->high = 0xffff; curqlevel->high = LTREE_MAX_LEVELS;
else if (state != LQPRS_WAITEND) else if (state != LQPRS_WAITEND)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error"), errmsg("lquery syntax error"),
errdetail("Unexpected end of line."))); errdetail("Unexpected end of line.")));
curqlevel = tmpql; curqlevel = tmpql;
...@@ -468,8 +486,8 @@ lquery_in(PG_FUNCTION_ARGS) ...@@ -468,8 +486,8 @@ lquery_in(PG_FUNCTION_ARGS)
else if (curqlevel->low > curqlevel->high) else if (curqlevel->low > curqlevel->high)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error"), errmsg("lquery syntax error"),
errdetail("Low limit(%d) is greater than upper(%d).", errdetail("Low limit (%d) is greater than upper (%d).",
curqlevel->low, curqlevel->high))); curqlevel->low, curqlevel->high)));
curqlevel = NEXTLEV(curqlevel); curqlevel = NEXTLEV(curqlevel);
...@@ -593,7 +611,7 @@ lquery_out(PG_FUNCTION_ARGS) ...@@ -593,7 +611,7 @@ lquery_out(PG_FUNCTION_ARGS)
} }
else if (curqlevel->low == 0) else if (curqlevel->low == 0)
{ {
if (curqlevel->high == 0xffff) if (curqlevel->high == LTREE_MAX_LEVELS)
{ {
*ptr = '*'; *ptr = '*';
*(ptr + 1) = '\0'; *(ptr + 1) = '\0';
...@@ -601,7 +619,7 @@ lquery_out(PG_FUNCTION_ARGS) ...@@ -601,7 +619,7 @@ lquery_out(PG_FUNCTION_ARGS)
else else
sprintf(ptr, "*{,%d}", curqlevel->high); sprintf(ptr, "*{,%d}", curqlevel->high);
} }
else if (curqlevel->high == 0xffff) else if (curqlevel->high == LTREE_MAX_LEVELS)
{ {
sprintf(ptr, "*{%d,}", curqlevel->low); sprintf(ptr, "*{%d,}", curqlevel->low);
} }
......
...@@ -274,10 +274,17 @@ static ltree * ...@@ -274,10 +274,17 @@ static ltree *
ltree_concat(ltree *a, ltree *b) ltree_concat(ltree *a, ltree *b)
{ {
ltree *r; 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); r = (ltree *) palloc0(VARSIZE(a) + VARSIZE(b) - LTREE_HDRSIZE);
SET_VARSIZE(r, 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(LTREE_FIRST(r), LTREE_FIRST(a), VARSIZE(a) - LTREE_HDRSIZE);
memcpy(((char *) LTREE_FIRST(r)) + VARSIZE(a) - LTREE_HDRSIZE, memcpy(((char *) LTREE_FIRST(r)) + VARSIZE(a) - LTREE_HDRSIZE,
......
...@@ -90,6 +90,17 @@ SELECT '1.*.4|3|2.*{1}'::lquery; ...@@ -90,6 +90,17 @@ SELECT '1.*.4|3|2.*{1}'::lquery;
SELECT 'qwerty%@*.tu'::lquery; SELECT 'qwerty%@*.tu'::lquery;
SELECT nlevel('1.2.3.4'); 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 '1.2'::ltree <= '2.2'::ltree; SELECT '1.2'::ltree <= '2.2'::ltree;
SELECT '2.2'::ltree = '2.2'::ltree; SELECT '2.2'::ltree = '2.2'::ltree;
......
...@@ -37,8 +37,7 @@ ...@@ -37,8 +37,7 @@
A <firstterm>label path</firstterm> is a sequence of zero or more A <firstterm>label path</firstterm> is a sequence of zero or more
labels separated by dots, for example <literal>L1.L2.L3</literal>, representing labels separated by dots, for example <literal>L1.L2.L3</literal>, representing
a path from the root of a hierarchical tree to a particular node. The 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 length of a label path cannot exceed 65535 labels.
preferable.
</para> </para>
<para> <para>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment