diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out index 28c321a4cf1..02cce5d925a 100644 --- a/contrib/ltree/expected/ltree.out +++ b/contrib/ltree/expected/ltree.out @@ -8089,3 +8089,13 @@ SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,23.*.2}' ; 15 (1 row) +-- 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: label string is too long +DETAIL: Label length is 1000, must be at most 255, at character 1001. +-- 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 0a44a8c4691..385db479540 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" @@ -338,7 +339,12 @@ parse_lquery(const char *buf) lptr++; lptr->start = ptr; state = LQPRS_WAITDELIM; - curqlevel->numvar++; + if (pg_add_u16_overflow(curqlevel->numvar, 1, &curqlevel->numvar)) + ereport(ERROR, + (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; @@ -530,7 +536,16 @@ parse_lquery(const char *buf) 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) + ereport(ERROR, + (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 2a612e347de..647f7f475f9 100644 --- a/contrib/ltree/sql/ltree.sql +++ b/contrib/ltree/sql/ltree.sql @@ -384,3 +384,11 @@ SELECT count(*) FROM _ltreetest WHERE t ~ '23.*{1}.1' ; SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.1' ; SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.2' ; SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,23.*.2}' ; + +-- 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;