1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Limit the number of nested NOT nodes in an fts5 expression to 256.

FossilOrigin-Name: 01219e69b430c8f5fea5ab6ce511ba8c9b4c9b32b6d2d36623dde99c3d3812c9
This commit is contained in:
dan
2023-05-15 17:24:48 +00:00
5 changed files with 112 additions and 10 deletions

View File

@ -17,6 +17,10 @@
#include "fts5Int.h"
#include "fts5parse.h"
#ifndef SQLITE_FTS5_MAX_EXPR_DEPTH
# define SQLITE_FTS5_MAX_EXPR_DEPTH 256
#endif
/*
** All token types in the generated fts5parse.h file are greater than 0.
*/
@ -57,11 +61,17 @@ struct Fts5Expr {
** FTS5_NOT (nChild, apChild valid)
** FTS5_STRING (pNear valid)
** FTS5_TERM (pNear valid)
**
** iHeight:
** Distance from this node to furthest leaf. This is always 0 for nodes
** of type FTS5_STRING and FTS5_TERM. For all other nodes it is one
** greater than the largest child value.
*/
struct Fts5ExprNode {
int eType; /* Node type */
int bEof; /* True at EOF */
int bNomatch; /* True if entry is not a match */
int iHeight; /* Distance to tree leaf nodes */
/* Next method for this node. */
int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64);
@ -131,6 +141,31 @@ struct Fts5Parse {
int bPhraseToAnd; /* Convert "a+b" to "a AND b" */
};
/*
** Check that the Fts5ExprNode.iHeight variables are set correctly in
** the expression tree passed as the only argument.
*/
#ifndef NDEBUG
static void assert_expr_depth_ok(int rc, Fts5ExprNode *p){
if( rc==SQLITE_OK ){
if( p->eType==FTS5_TERM || p->eType==FTS5_STRING || p->eType==0 ){
assert( p->iHeight==0 );
}else{
int ii;
int iMaxChild = 0;
for(ii=0; ii<p->nChild; ii++){
Fts5ExprNode *pChild = p->apChild[ii];
iMaxChild = MAX(iMaxChild, pChild->iHeight);
assert_expr_depth_ok(SQLITE_OK, pChild);
}
assert( p->iHeight==iMaxChild+1 );
}
}
}
#else
# define assert_expr_depth_ok(rc, p)
#endif
void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){
va_list ap;
va_start(ap, zFmt);
@ -245,6 +280,8 @@ int sqlite3Fts5ExprNew(
}while( sParse.rc==SQLITE_OK && t!=FTS5_EOF );
sqlite3Fts5ParserFree(pEngine, fts5ParseFree);
assert_expr_depth_ok(sParse.rc, sParse.pExpr);
/* If the LHS of the MATCH expression was a user column, apply the
** implicit column-filter. */
if( iCol<pConfig->nCol && sParse.pExpr && sParse.rc==SQLITE_OK ){
@ -2206,6 +2243,7 @@ static void fts5ExprAssignXNext(Fts5ExprNode *pNode){
}
static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
int ii = p->nChild;
if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){
int nByte = sizeof(Fts5ExprNode*) * pSub->nChild;
memcpy(&p->apChild[p->nChild], pSub->apChild, nByte);
@ -2214,6 +2252,9 @@ static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
}else{
p->apChild[p->nChild++] = pSub;
}
for( ; ii<p->nChild; ii++){
p->iHeight = MAX(p->iHeight, p->apChild[ii]->iHeight + 1);
}
}
/*
@ -2244,6 +2285,7 @@ static Fts5ExprNode *fts5ParsePhraseToAnd(
if( pRet ){
pRet->eType = FTS5_AND;
pRet->nChild = nTerm;
pRet->iHeight = 1;
fts5ExprAssignXNext(pRet);
pParse->nPhrase--;
for(ii=0; ii<nTerm; ii++){
@ -2349,6 +2391,14 @@ Fts5ExprNode *sqlite3Fts5ParseNode(
}else{
fts5ExprAddChildren(pRet, pLeft);
fts5ExprAddChildren(pRet, pRight);
if( pRet->iHeight>SQLITE_FTS5_MAX_EXPR_DEPTH ){
sqlite3Fts5ParseError(pParse,
"fts5 expression tree is too large (maximum depth %d)",
SQLITE_FTS5_MAX_EXPR_DEPTH
);
sqlite3_free(pRet);
pRet = 0;
}
}
}
}

View File

@ -180,7 +180,11 @@ if {[detail_is_full]} {
} {1 2}
}
do_execsql_test 4.5 {
do_execsql_test 4.5.1 {
SELECT rowid FROM s1 WHERE s1 MATCH 'a AND x'
} {1 2}
do_execsql_test 4.5.2 {
SELECT rowid FROM s1 WHERE s1 MATCH 'a x'
} {1 2}

View File

@ -0,0 +1,47 @@
# 2023 May 16
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5limits
return_if_no_fts5
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE ft USING fts5(x);
}
# Default limit for expression depth is 256
#
foreach {tn nRepeat op bErr} {
1 200 AND 0
2 200 NOT 0
3 200 OR 0
4 260 AND 0
5 260 NOT 1
6 260 OR 0
} {
set L [string repeat "abc " $nRepeat]
set Q [join $L " $op "]
set res {0 {}}
if {$bErr} {
set res "1 {fts5 expression tree is too large (maximum depth 256)}"
}
do_catchsql_test 1.$tn {
SELECT * FROM ft($Q)
} $res
}
finish_test