diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out index d2a56628475..53ddf6ca330 100644 --- a/contrib/ltree/expected/ltree.out +++ b/contrib/ltree/expected/ltree.out @@ -8202,3 +8202,13 @@ FROM (VALUES ('.2.3', 'ltree'), !tree & aWdf@* | ltxtquery | t | | | | (8 rows) +-- Test for overflow of lquery_level.totallen, based on an lquery level with +-- many OR-variants. +SELECT (repeat('x', 1000) || repeat('|' || repeat('x', 1000), 65))::lquery; +ERROR: lquery level is too large +DETAIL: Total size of level exceeds the maximum allowed (65535 bytes). +-- Test for overflow of lquery_level.numvar, with a set of single-char +-- variants in one level. +SELECT (repeat('a|', 65535) || 'a')::lquery; +ERROR: lquery level has too many variants +DETAIL: Number of variants exceeds the maximum allowed (65535). diff --git a/contrib/ltree/ltree_io.c b/contrib/ltree/ltree_io.c index 54c4ca3c5c3..88d2ef0c917 100644 --- a/contrib/ltree/ltree_io.c +++ b/contrib/ltree/ltree_io.c @@ -7,6 +7,7 @@ #include +#include "common/int.h" #include "crc32.h" #include "libpq/pqformat.h" #include "ltree.h" @@ -344,7 +345,12 @@ parse_lquery(const char *buf, struct Node *escontext) lptr++; lptr->start = ptr; state = LQPRS_WAITDELIM; - curqlevel->numvar++; + if (pg_add_u16_overflow(curqlevel->numvar, 1, &curqlevel->numvar)) + ereturn(escontext, NULL, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("lquery level has too many variants"), + errdetail("Number of variants exceeds the maximum allowed (%d).", + PG_UINT16_MAX))); } else UNCHAR; @@ -542,7 +548,16 @@ parse_lquery(const char *buf, struct Node *escontext) lptr = GETVAR(curqlevel); while (lptr - GETVAR(curqlevel) < curqlevel->numvar) { - cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len); + int newlen = cur->totallen + MAXALIGN(LVAR_HDRSIZE + lptr->len); + + if (newlen > PG_UINT16_MAX) + ereturn(escontext, NULL, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("lquery level is too large"), + errdetail("Total size of level exceeds the maximum allowed (%d bytes).", + PG_UINT16_MAX))); + cur->totallen = (uint16) newlen; + lrptr->len = lptr->len; lrptr->flag = lptr->flag; lrptr->val = ltree_crc32_sz(lptr->start, lptr->len); diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql index 77e6958c62a..fb3cca163a2 100644 --- a/contrib/ltree/sql/ltree.sql +++ b/contrib/ltree/sql/ltree.sql @@ -457,3 +457,11 @@ FROM (VALUES ('.2.3', 'ltree'), ('!tree & aWdf@*','ltxtquery')) AS a(str,typ), LATERAL pg_input_error_info(a.str, a.typ) as errinfo; + +-- Test for overflow of lquery_level.totallen, based on an lquery level with +-- many OR-variants. +SELECT (repeat('x', 1000) || repeat('|' || repeat('x', 1000), 65))::lquery; + +-- Test for overflow of lquery_level.numvar, with a set of single-char +-- variants in one level. +SELECT (repeat('a|', 65535) || 'a')::lquery;