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

Commit 8983852

Browse files
committed
Improving various checks by Heikki Linnakangas <heikki@enterprisedb.com>
- add code to check that the query tree is well-formed. It was indeed possible to send malformed queries in binary mode, which produced all kinds of strange results. - make the left-field a uint32. There's no reason to arbitrarily limit it to 16-bits, and it won't increase the disk/memory footprint either now that QueryOperator and QueryOperand are separate structs. - add check_stack_depth() call to all recursive functions I found. Some of them might have a natural limit so that you can't force arbitrarily deep recursions, but check_stack_depth() is cheap enough that seems best to just stick it into anything that might be a problem.
1 parent e5be899 commit 8983852

File tree

6 files changed

+116
-24
lines changed

6 files changed

+116
-24
lines changed

src/backend/utils/adt/tsquery.c

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/utils/adt/tsquery.c,v 1.3 2007/09/07 15:09:56 teodor Exp $
10+
* $PostgreSQL: pgsql/src/backend/utils/adt/tsquery.c,v 1.4 2007/09/07 15:35:10 teodor Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -21,6 +21,7 @@
2121
#include "tsearch/ts_utils.h"
2222
#include "utils/memutils.h"
2323
#include "utils/pg_crc.h"
24+
#include "nodes/bitmapset.h"
2425

2526

2627
struct TSQueryParserStateData
@@ -388,14 +389,14 @@ makepol(TSQueryParserState state,
388389
* QueryItems must be in polish (prefix) notation.
389390
*/
390391
static void
391-
findoprnd(QueryItem *ptr, int *pos)
392+
findoprnd(QueryItem *ptr, uint32 *pos)
392393
{
393394
/* since this function recurses, it could be driven to stack overflow. */
394395
check_stack_depth();
395396

396397
if (ptr[*pos].type == QI_VAL ||
397398
ptr[*pos].type == QI_VALSTOP) /* need to handle VALSTOP here,
398-
* they haven't been cleansed
399+
* they haven't been cleaned
399400
* away yet.
400401
*/
401402
{
@@ -451,7 +452,7 @@ parse_tsquery(char *buf,
451452
TSQuery query;
452453
int commonlen;
453454
QueryItem *ptr;
454-
int pos = 0;
455+
uint32 pos = 0;
455456
ListCell *cell;
456457

457458
/* init state */
@@ -792,14 +793,15 @@ tsqueryrecv(PG_FUNCTION_ARGS)
792793
QueryItem *item;
793794
int datalen = 0;
794795
char *ptr;
796+
Bitmapset *parentset = NULL;
795797

796798
size = pq_getmsgint(buf, sizeof(uint32));
797799
if (size < 0 || size > (MaxAllocSize / sizeof(QueryItem)))
798800
elog(ERROR, "invalid size of tsquery");
799801

800802
len = HDRSIZETQ + sizeof(QueryItem) * size;
801803

802-
query = (TSQuery) palloc(len);
804+
query = (TSQuery) palloc0(len);
803805
query->size = size;
804806
item = GETQUERY(query);
805807

@@ -814,6 +816,15 @@ tsqueryrecv(PG_FUNCTION_ARGS)
814816
item->operand.valcrc = (int32) pq_getmsgint(buf, sizeof(int32));
815817
item->operand.length = pq_getmsgint(buf, sizeof(int16));
816818

819+
/* Check that the weight bitmap is valid */
820+
if (item->operand.weight < 0 || item->operand.weight > 0xF)
821+
elog(ERROR, "invalid weight bitmap");
822+
823+
/* XXX: We don't check that the CRC is valid. Actually, if we
824+
* bothered to calculate it to verify, there would be no need
825+
* to transfer it.
826+
*/
827+
817828
/*
818829
* Check that datalen doesn't grow too large. Without the
819830
* check, a malicious client could induce a buffer overflow
@@ -828,7 +839,7 @@ tsqueryrecv(PG_FUNCTION_ARGS)
828839
elog(ERROR, "invalid tsquery; total operand length exceeded");
829840

830841
/* We can calculate distance from datalen, no need to send it
831-
* through the wire. If we did, we would have to check that
842+
* across the wire. If we did, we would have to check that
832843
* it's valid anyway.
833844
*/
834845
item->operand.distance = datalen;
@@ -842,24 +853,41 @@ tsqueryrecv(PG_FUNCTION_ARGS)
842853
item->operator.oper != OP_OR &&
843854
item->operator.oper != OP_AND)
844855
elog(ERROR, "unknown operator type %d", (int) item->operator.oper);
856+
857+
/*
858+
* Check that no previous operator node points to the right
859+
* operand. That would mean that the operand node
860+
* has two parents.
861+
*/
862+
if (bms_is_member(i + 1, parentset))
863+
elog(ERROR, "malformed query tree");
864+
865+
parentset = bms_add_member(parentset, i + 1);
866+
845867
if(item->operator.oper != OP_NOT)
846868
{
847-
item->operator.left = (int16) pq_getmsgint(buf, sizeof(int16));
869+
uint32 left = (uint32) pq_getmsgint(buf, sizeof(uint32));
870+
848871
/*
849-
* Sanity checks
872+
* Right operand is implicitly at "this+1". Don't allow
873+
* left to point to the right operand, or to self.
850874
*/
851-
if (item->operator.left <= 0 || i + item->operator.left >= size)
875+
if (left <= 1 || i + left >= size)
852876
elog(ERROR, "invalid pointer to left operand");
853877

854-
/* XXX: Though there's no way to construct a TSQuery that's
855-
* not in polish notation, we don't enforce that for
856-
* queries received from client in binary mode. Is there
857-
* anything that relies on it?
858-
*
859-
* XXX: The tree could be malformed in other ways too,
860-
* a node could have two parents, for example.
878+
/*
879+
* Check that no previous operator node points to the left
880+
* operand.
861881
*/
882+
if (bms_is_member(i + left, parentset))
883+
elog(ERROR, "malformed query tree");
884+
885+
parentset = bms_add_member(parentset, i + left);
886+
887+
item->operator.left = left;
862888
}
889+
else
890+
item->operator.left = 1; /* do not leave uninitialized fields */
863891

864892
if (i == size - 1)
865893
elog(ERROR, "invalid pointer to right operand");
@@ -871,6 +899,13 @@ tsqueryrecv(PG_FUNCTION_ARGS)
871899
item++;
872900
}
873901

902+
/* Now check that each node, except the root, has a parent. We
903+
* already checked above that no node has more than one parent. */
904+
if (bms_num_members(parentset) != size - 1 && size != 0)
905+
elog(ERROR, "malformed query tree");
906+
907+
bms_free( parentset );
908+
874909
query = (TSQuery) repalloc(query, len + datalen);
875910

876911
item = GETQUERY(query);

src/backend/utils/adt/tsquery_cleanup.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/adt/tsquery_cleanup.c,v 1.2 2007/09/07 15:09:56 teodor Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/adt/tsquery_cleanup.c,v 1.3 2007/09/07 15:35:10 teodor Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -57,6 +57,9 @@ typedef struct
5757
static void
5858
plainnode(PLAINTREE * state, NODE * node)
5959
{
60+
/* since this function recurses, it could be driven to stack overflow. */
61+
check_stack_depth();
62+
6063
if (state->cur == state->len)
6164
{
6265
state->len *= 2;
@@ -107,6 +110,9 @@ plaintree(NODE * root, int *len)
107110
static void
108111
freetree(NODE * node)
109112
{
113+
/* since this function recurses, it could be driven to stack overflow. */
114+
check_stack_depth();
115+
110116
if (!node)
111117
return;
112118
if (node->left)
@@ -125,6 +131,9 @@ freetree(NODE * node)
125131
static NODE *
126132
clean_NOT_intree(NODE * node)
127133
{
134+
/* since this function recurses, it could be driven to stack overflow. */
135+
check_stack_depth();
136+
128137
if (node->valnode->type == QI_VAL)
129138
return node;
130139

@@ -203,6 +212,9 @@ clean_fakeval_intree(NODE * node, char *result)
203212
char lresult = V_UNKNOWN,
204213
rresult = V_UNKNOWN;
205214

215+
/* since this function recurses, it could be driven to stack overflow. */
216+
check_stack_depth();
217+
206218
if (node->valnode->type == QI_VAL)
207219
return node;
208220
else

src/backend/utils/adt/tsquery_rewrite.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/utils/adt/tsquery_rewrite.c,v 1.2 2007/09/07 15:09:56 teodor Exp $
10+
* $PostgreSQL: pgsql/src/backend/utils/adt/tsquery_rewrite.c,v 1.3 2007/09/07 15:35:10 teodor Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -22,6 +22,9 @@
2222
static int
2323
addone(int *counters, int last, int total)
2424
{
25+
/* since this function recurses, it could be driven to stack overflow. */
26+
check_stack_depth();
27+
2528
counters[last]++;
2629
if (counters[last] >= total)
2730
{
@@ -173,6 +176,9 @@ findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
173176
static QTNode *
174177
dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
175178
{
179+
/* since this function recurses, it could be driven to stack overflow. */
180+
check_stack_depth();
181+
176182
root = findeq(root, ex, subs, isfind);
177183

178184
if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == QI_OPR)

src/backend/utils/adt/tsquery_util.c

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/utils/adt/tsquery_util.c,v 1.2 2007/09/07 15:09:56 teodor Exp $
10+
* $PostgreSQL: pgsql/src/backend/utils/adt/tsquery_util.c,v 1.3 2007/09/07 15:35:10 teodor Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -22,6 +22,9 @@ QT2QTN(QueryItem * in, char *operand)
2222
{
2323
QTNode *node = (QTNode *) palloc0(sizeof(QTNode));
2424

25+
/* since this function recurses, it could be driven to stack overflow. */
26+
check_stack_depth();
27+
2528
node->valnode = in;
2629

2730
if (in->type == QI_OPR)
@@ -53,6 +56,9 @@ QTNFree(QTNode * in)
5356
if (!in)
5457
return;
5558

59+
/* since this function recurses, it could be driven to stack overflow. */
60+
check_stack_depth();
61+
5662
if (in->valnode->type == QI_VAL && in->word && (in->flags & QTN_WORDFREE) != 0)
5763
pfree(in->word);
5864

@@ -79,6 +85,9 @@ QTNFree(QTNode * in)
7985
int
8086
QTNodeCompare(QTNode * an, QTNode * bn)
8187
{
88+
/* since this function recurses, it could be driven to stack overflow. */
89+
check_stack_depth();
90+
8291
if (an->valnode->type != bn->valnode->type)
8392
return (an->valnode->type > bn->valnode->type) ? -1 : 1;
8493

@@ -133,6 +142,9 @@ QTNSort(QTNode * in)
133142
{
134143
int i;
135144

145+
/* since this function recurses, it could be driven to stack overflow. */
146+
check_stack_depth();
147+
136148
if (in->valnode->type != QI_OPR)
137149
return;
138150

@@ -165,6 +177,9 @@ QTNTernary(QTNode * in)
165177
{
166178
int i;
167179

180+
/* since this function recurses, it could be driven to stack overflow. */
181+
check_stack_depth();
182+
168183
if (in->valnode->type != QI_OPR)
169184
return;
170185

@@ -205,6 +220,9 @@ QTNBinary(QTNode * in)
205220
{
206221
int i;
207222

223+
/* since this function recurses, it could be driven to stack overflow. */
224+
check_stack_depth();
225+
208226
if (in->valnode->type != QI_OPR)
209227
return;
210228

@@ -244,6 +262,9 @@ QTNBinary(QTNode * in)
244262
static void
245263
cntsize(QTNode * in, int *sumlen, int *nnode)
246264
{
265+
/* since this function recurses, it could be driven to stack overflow. */
266+
check_stack_depth();
267+
247268
*nnode += 1;
248269
if (in->valnode->type == QI_OPR)
249270
{
@@ -268,6 +289,9 @@ typedef struct
268289
static void
269290
fillQT(QTN2QTState *state, QTNode *in)
270291
{
292+
/* since this function recurses, it could be driven to stack overflow. */
293+
check_stack_depth();
294+
271295
if (in->valnode->type == QI_VAL)
272296
{
273297
memcpy(state->curitem, in->valnode, sizeof(QueryOperand));
@@ -325,7 +349,12 @@ QTN2QT(QTNode *in)
325349
QTNode *
326350
QTNCopy(QTNode *in)
327351
{
328-
QTNode *out = (QTNode *) palloc(sizeof(QTNode));
352+
QTNode *out;
353+
354+
/* since this function recurses, it could be driven to stack overflow. */
355+
check_stack_depth();
356+
357+
out = (QTNode *) palloc(sizeof(QTNode));
329358

330359
*out = *in;
331360
out->valnode = (QueryItem *) palloc(sizeof(QueryItem));

src/backend/utils/adt/tsrank.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/utils/adt/tsrank.c,v 1.2 2007/09/07 15:09:56 teodor Exp $
10+
* $PostgreSQL: pgsql/src/backend/utils/adt/tsrank.c,v 1.3 2007/09/07 15:35:10 teodor Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -508,6 +508,10 @@ Cover(DocRepresentation *doc, int len, TSQuery query, Extention *ext)
508508
int i;
509509
bool found = false;
510510

511+
/* since this function recurses, it could be driven to stack overflow.
512+
* (though any decent compiler will optimize away the tail-recursion. */
513+
check_stack_depth();
514+
511515
reset_istrue_flag(query);
512516

513517
ext->p = 0x7fffffff;

src/include/tsearch/ts_type.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*
66
* Copyright (c) 1998-2007, PostgreSQL Global Development Group
77
*
8-
* $PostgreSQL: pgsql/src/include/tsearch/ts_type.h,v 1.2 2007/09/07 15:09:56 teodor Exp $
8+
* $PostgreSQL: pgsql/src/include/tsearch/ts_type.h,v 1.3 2007/09/07 15:35:11 teodor Exp $
99
*
1010
*-------------------------------------------------------------------------
1111
*/
@@ -160,7 +160,13 @@ typedef struct
160160
{
161161
QueryItemType type; /* operand or kind of operator (ts_tokentype) */
162162
int8 weight; /* weights of operand to search. It's a bitmask of allowed weights.
163-
* if it =0 then any weight are allowed */
163+
* if it =0 then any weight are allowed.
164+
* Weights and bit map:
165+
* A: 1<<3
166+
* B: 1<<2
167+
* C: 1<<1
168+
* D: 1<<0
169+
*/
164170
int32 valcrc; /* XXX: pg_crc32 would be a more appropriate data type,
165171
* but we use comparisons to signed integers in the code.
166172
* They would need to be changed as well. */
@@ -182,7 +188,7 @@ typedef struct
182188
{
183189
QueryItemType type;
184190
int8 oper; /* see above */
185-
int16 left; /* pointer to left operand. Right operand is
191+
uint32 left; /* pointer to left operand. Right operand is
186192
* item + 1, left operand is placed
187193
* item+item->left */
188194
} QueryOperator;

0 commit comments

Comments
 (0)