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:
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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}
|
||||
|
||||
|
47
ext/fts5/test/fts5limits.test
Normal file
47
ext/fts5/test/fts5limits.test
Normal 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
|
||||
|
Reference in New Issue
Block a user