Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 95f7ddf

Browse files
committed
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
1 parent 42750b0 commit 95f7ddf

File tree

6 files changed

+104
-21
lines changed

6 files changed

+104
-21
lines changed

contrib/ltree/expected/ltree.out

+46
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,52 @@ SELECT nlevel('1.2.3.4');
457457
4
458458
(1 row)
459459

460+
SELECT nlevel(('1' || repeat('.1', 65534))::ltree);
461+
nlevel
462+
--------
463+
65535
464+
(1 row)
465+
466+
SELECT nlevel(('1' || repeat('.1', 65535))::ltree);
467+
ERROR: number of ltree levels (65536) exceeds the maximum allowed (65535)
468+
SELECT nlevel(('1' || repeat('.1', 65534))::ltree || '1');
469+
ERROR: number of ltree levels (65536) exceeds the maximum allowed (65535)
470+
SELECT ('1' || repeat('.1', 65534))::lquery IS NULL;
471+
?column?
472+
----------
473+
f
474+
(1 row)
475+
476+
SELECT ('1' || repeat('.1', 65535))::lquery IS NULL;
477+
ERROR: number of lquery levels (65536) exceeds the maximum allowed (65535)
478+
SELECT '*{65535}'::lquery;
479+
lquery
480+
----------
481+
*{65535}
482+
(1 row)
483+
484+
SELECT '*{65536}'::lquery;
485+
ERROR: lquery syntax error
486+
LINE 1: SELECT '*{65536}'::lquery;
487+
^
488+
DETAIL: Low limit (65536) exceeds the maximum allowed (65535).
489+
SELECT '*{,65534}'::lquery;
490+
lquery
491+
-----------
492+
*{,65534}
493+
(1 row)
494+
495+
SELECT '*{,65535}'::lquery;
496+
lquery
497+
--------
498+
*
499+
(1 row)
500+
501+
SELECT '*{,65536}'::lquery;
502+
ERROR: lquery syntax error
503+
LINE 1: SELECT '*{,65536}'::lquery;
504+
^
505+
DETAIL: High limit (65536) exceeds the maximum allowed (65535).
460506
SELECT '1.2'::ltree < '2.2'::ltree;
461507
?column?
462508
----------

contrib/ltree/ltree.h

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ typedef struct
2525

2626
#define LTREE_HDRSIZE MAXALIGN( offsetof(ltree, data) )
2727
#define LTREE_FIRST(x) ( (ltree_level*)( ((char*)(x))+LTREE_HDRSIZE ) )
28+
#define LTREE_MAX_LEVELS PG_UINT16_MAX /* ltree.numlevel is uint16 */
2829

2930

3031
/* lquery */
@@ -77,6 +78,7 @@ typedef struct
7778

7879
#define LQUERY_HDRSIZE MAXALIGN( offsetof(lquery, data) )
7980
#define LQUERY_FIRST(x) ( (lquery_level*)( ((char*)(x))+LQUERY_HDRSIZE ) )
81+
#define LQUERY_MAX_LEVELS PG_UINT16_MAX /* lquery.numlevel is uint16 */
8082

8183
#define LQUERY_HASNOT 0x01
8284

contrib/ltree/ltree_io.c

+36-18
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ ltree_in(PG_FUNCTION_ARGS)
5858
ptr += charlen;
5959
}
6060

61-
if (num + 1 > MaxAllocSize / sizeof(nodeitem))
61+
if (num + 1 > LTREE_MAX_LEVELS)
6262
ereport(ERROR,
6363
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
64-
errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
65-
num + 1, (int) (MaxAllocSize / sizeof(nodeitem)))));
64+
errmsg("number of ltree levels (%d) exceeds the maximum allowed (%d)",
65+
num + 1, LTREE_MAX_LEVELS)));
6666
list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
6767
ptr = buf;
6868
while (*ptr)
@@ -227,11 +227,11 @@ lquery_in(PG_FUNCTION_ARGS)
227227
}
228228

229229
num++;
230-
if (num > MaxAllocSize / ITEMSIZE)
230+
if (num > LQUERY_MAX_LEVELS)
231231
ereport(ERROR,
232232
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
233-
errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
234-
num, (int) (MaxAllocSize / ITEMSIZE))));
233+
errmsg("number of lquery levels (%d) exceeds the maximum allowed (%d)",
234+
num, LQUERY_MAX_LEVELS)));
235235
curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
236236
ptr = buf;
237237
while (*ptr)
@@ -344,7 +344,7 @@ lquery_in(PG_FUNCTION_ARGS)
344344
else if (charlen == 1 && t_iseq(ptr, '.'))
345345
{
346346
curqlevel->low = 0;
347-
curqlevel->high = 0xffff;
347+
curqlevel->high = LTREE_MAX_LEVELS;
348348
curqlevel = NEXTLEV(curqlevel);
349349
state = LQPRS_WAITLEVEL;
350350
}
@@ -357,7 +357,16 @@ lquery_in(PG_FUNCTION_ARGS)
357357
state = LQPRS_WAITSNUM;
358358
else if (t_isdigit(ptr))
359359
{
360-
curqlevel->low = atoi(ptr);
360+
int low = atoi(ptr);
361+
362+
if (low < 0 || low > LTREE_MAX_LEVELS)
363+
ereport(ERROR,
364+
(errcode(ERRCODE_SYNTAX_ERROR),
365+
errmsg("lquery syntax error"),
366+
errdetail("Low limit (%d) exceeds the maximum allowed (%d).",
367+
low, LTREE_MAX_LEVELS)));
368+
369+
curqlevel->low = (uint16) low;
361370
state = LQPRS_WAITND;
362371
}
363372
else
@@ -367,12 +376,21 @@ lquery_in(PG_FUNCTION_ARGS)
367376
{
368377
if (t_isdigit(ptr))
369378
{
370-
curqlevel->high = atoi(ptr);
379+
int high = atoi(ptr);
380+
381+
if (high < 0 || high > LTREE_MAX_LEVELS)
382+
ereport(ERROR,
383+
(errcode(ERRCODE_SYNTAX_ERROR),
384+
errmsg("lquery syntax error"),
385+
errdetail("High limit (%d) exceeds the maximum allowed (%d).",
386+
high, LTREE_MAX_LEVELS)));
387+
388+
curqlevel->high = (uint16) high;
371389
state = LQPRS_WAITCLOSE;
372390
}
373391
else if (charlen == 1 && t_iseq(ptr, '}'))
374392
{
375-
curqlevel->high = 0xffff;
393+
curqlevel->high = LTREE_MAX_LEVELS;
376394
state = LQPRS_WAITEND;
377395
}
378396
else
@@ -422,7 +440,7 @@ lquery_in(PG_FUNCTION_ARGS)
422440
if (lptr->start == ptr)
423441
ereport(ERROR,
424442
(errcode(ERRCODE_SYNTAX_ERROR),
425-
errmsg("syntax error"),
443+
errmsg("lquery syntax error"),
426444
errdetail("Unexpected end of line.")));
427445

428446
lptr->len = ptr - lptr->start -
@@ -432,7 +450,7 @@ lquery_in(PG_FUNCTION_ARGS)
432450
if (lptr->len == 0)
433451
ereport(ERROR,
434452
(errcode(ERRCODE_SYNTAX_ERROR),
435-
errmsg("syntax error"),
453+
errmsg("lquery syntax error"),
436454
errdetail("Unexpected end of line.")));
437455

438456
if (lptr->wlen > 255)
@@ -444,11 +462,11 @@ lquery_in(PG_FUNCTION_ARGS)
444462
lptr->wlen, pos)));
445463
}
446464
else if (state == LQPRS_WAITOPEN)
447-
curqlevel->high = 0xffff;
465+
curqlevel->high = LTREE_MAX_LEVELS;
448466
else if (state != LQPRS_WAITEND)
449467
ereport(ERROR,
450468
(errcode(ERRCODE_SYNTAX_ERROR),
451-
errmsg("syntax error"),
469+
errmsg("lquery syntax error"),
452470
errdetail("Unexpected end of line.")));
453471

454472
curqlevel = tmpql;
@@ -468,8 +486,8 @@ lquery_in(PG_FUNCTION_ARGS)
468486
else if (curqlevel->low > curqlevel->high)
469487
ereport(ERROR,
470488
(errcode(ERRCODE_SYNTAX_ERROR),
471-
errmsg("syntax error"),
472-
errdetail("Low limit(%d) is greater than upper(%d).",
489+
errmsg("lquery syntax error"),
490+
errdetail("Low limit (%d) is greater than upper (%d).",
473491
curqlevel->low, curqlevel->high)));
474492

475493
curqlevel = NEXTLEV(curqlevel);
@@ -593,15 +611,15 @@ lquery_out(PG_FUNCTION_ARGS)
593611
}
594612
else if (curqlevel->low == 0)
595613
{
596-
if (curqlevel->high == 0xffff)
614+
if (curqlevel->high == LTREE_MAX_LEVELS)
597615
{
598616
*ptr = '*';
599617
*(ptr + 1) = '\0';
600618
}
601619
else
602620
sprintf(ptr, "*{,%d}", curqlevel->high);
603621
}
604-
else if (curqlevel->high == 0xffff)
622+
else if (curqlevel->high == LTREE_MAX_LEVELS)
605623
{
606624
sprintf(ptr, "*{%d,}", curqlevel->low);
607625
}

contrib/ltree/ltree_op.c

+8-1
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,17 @@ static ltree *
274274
ltree_concat(ltree *a, ltree *b)
275275
{
276276
ltree *r;
277+
int numlevel = (int) a->numlevel + b->numlevel;
278+
279+
if (numlevel > LTREE_MAX_LEVELS)
280+
ereport(ERROR,
281+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
282+
errmsg("number of ltree levels (%d) exceeds the maximum allowed (%d)",
283+
numlevel, LTREE_MAX_LEVELS)));
277284

278285
r = (ltree *) palloc0(VARSIZE(a) + VARSIZE(b) - LTREE_HDRSIZE);
279286
SET_VARSIZE(r, VARSIZE(a) + VARSIZE(b) - LTREE_HDRSIZE);
280-
r->numlevel = a->numlevel + b->numlevel;
287+
r->numlevel = (uint16) numlevel;
281288

282289
memcpy(LTREE_FIRST(r), LTREE_FIRST(a), VARSIZE(a) - LTREE_HDRSIZE);
283290
memcpy(((char *) LTREE_FIRST(r)) + VARSIZE(a) - LTREE_HDRSIZE,

contrib/ltree/sql/ltree.sql

+11
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ SELECT '1.*.4|3|2.*{1}'::lquery;
9090
SELECT 'qwerty%@*.tu'::lquery;
9191

9292
SELECT nlevel('1.2.3.4');
93+
SELECT nlevel(('1' || repeat('.1', 65534))::ltree);
94+
SELECT nlevel(('1' || repeat('.1', 65535))::ltree);
95+
SELECT nlevel(('1' || repeat('.1', 65534))::ltree || '1');
96+
SELECT ('1' || repeat('.1', 65534))::lquery IS NULL;
97+
SELECT ('1' || repeat('.1', 65535))::lquery IS NULL;
98+
SELECT '*{65535}'::lquery;
99+
SELECT '*{65536}'::lquery;
100+
SELECT '*{,65534}'::lquery;
101+
SELECT '*{,65535}'::lquery;
102+
SELECT '*{,65536}'::lquery;
103+
93104
SELECT '1.2'::ltree < '2.2'::ltree;
94105
SELECT '1.2'::ltree <= '2.2'::ltree;
95106
SELECT '2.2'::ltree = '2.2'::ltree;

doc/src/sgml/ltree.sgml

+1-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@
3737
A <firstterm>label path</firstterm> is a sequence of zero or more
3838
labels separated by dots, for example <literal>L1.L2.L3</literal>, representing
3939
a path from the root of a hierarchical tree to a particular node. The
40-
length of a label path must be less than 65kB, but keeping it under 2kB is
41-
preferable.
40+
length of a label path cannot exceed 65535 labels.
4241
</para>
4342

4443
<para>

0 commit comments

Comments
 (0)