]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Limit the number of nested NOT nodes in an fts5 expression to 256. fts5-expr-limit
authordan <Dan Kennedy>
Mon, 15 May 2023 17:14:16 +0000 (17:14 +0000)
committerdan <Dan Kennedy>
Mon, 15 May 2023 17:14:16 +0000 (17:14 +0000)
FossilOrigin-Name: 0e5c1ee40a146ef8b2b3c5f53d0a45e092bc8d8e933f3819805c995819d31bae

ext/fts5/fts5_expr.c
ext/fts5/test/fts5ab.test
ext/fts5/test/fts5limits.test [new file with mode: 0644]
manifest
manifest.uuid

index e87650d47dc4bacd9aca984babcb3d9747bee714..0e018420d0713048ef059c1296fb86d859852c8f 100644 (file)
 #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;
+          }
         }
       }
     }
index 3979dd44beb5011332642e643070167ef5b07d63..5aa7456586707d4b9f2e682d042263d542a0886e 100644 (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}
 
diff --git a/ext/fts5/test/fts5limits.test b/ext/fts5/test/fts5limits.test
new file mode 100644 (file)
index 0000000..90d175a
--- /dev/null
@@ -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
+
index 7a351492c3dafb905847723b9573067e5015b4ec..ae8691978f4a6ed00b26dbb0e1de8142bd83055b 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\san\s"#ifndef\sSQLITE_HAVE_SQLITE3R"\sdirective\sto\sshell.c.in,\sto\savoid\sincluding\sthe\srecovery\scode\sa\ssecond\stime\sif\sthe\sshell\sis\sbeing\scompiled\swith\ssqlite3r.c.
-D 2023-05-13T19:13:40.599
+C Limit\sthe\snumber\sof\snested\sNOT\snodes\sin\san\sfts5\sexpression\sto\s256.
+D 2023-05-15T17:14:16.045
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -90,7 +90,7 @@ F ext/fts5/fts5Int.h ed48a096418ff4a7c02ac9bd1e8d40c46de21b79a132b8b08d3f3223370
 F ext/fts5/fts5_aux.c 572d5ec92ba7301df2fea3258576332f2f4d2dfd66d8263afd157d9deceac480
 F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5
 F ext/fts5/fts5_config.c 051056a9052f5d3a4d1c695f996fd364f920e341f136c60ab2c04aa7e267113f
-F ext/fts5/fts5_expr.c 7d298d76ea010c339b26ca47f6f69e9aef520ea46c083deaa4e83e87cf0e94b1
+F ext/fts5/fts5_expr.c 58fb8ceddfb1cefcd54510f9f2f33c220ef9d1b3fa77462111f5ae2a825ab7b1
 F ext/fts5/fts5_hash.c d4fb70940359f2120ccd1de7ffe64cc3efe65de9e8995b822cd536ff64c96982
 F ext/fts5/fts5_index.c de3cdae2e0056594aad97a728be5c43b6d7a6cdc7e9cc16f197892b2d8689c21
 F ext/fts5/fts5_main.c b4dba04a36aaf9b8e8cef0100b6dbb422cc74753eacc11d6401cac7a87c0f38d
@@ -106,7 +106,7 @@ F ext/fts5/fts5parse.y eb526940f892ade5693f22ffd6c4f2702543a9059942772526eac1fde
 F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
 F ext/fts5/test/fts5_common.tcl a9de9c2209cc4e7ae3c753e783504e67206c6c1467d08f209cd0c5923d3e8d8b
 F ext/fts5/test/fts5aa.test 5bd43427b7d08ce2e19c488a26534be450538b9232d4d5305049e8de236e9aa9
-F ext/fts5/test/fts5ab.test 9205c839332c908aaad2b01ab8670ece8b161e8f2ec8a9fabf18ca9385880bb7
+F ext/fts5/test/fts5ab.test bd932720c748383277456b81f91bc00453de2174f9762cd05f95d0495dc50390
 F ext/fts5/test/fts5ac.test a7aa7e1fefc6e1918aa4d3111d5c44a09177168e962c5fd2cca9620de8a7ed6d
 F ext/fts5/test/fts5ad.test e8cf959dfcd57c8e46d6f5f25665686f3b6627130a9a981371dafdf6482790de
 F ext/fts5/test/fts5ae.test 1142d16d9cc193894dc13cc8f9c7a8a21411ac61b5567a878514df6f9f0d7bb7
@@ -166,6 +166,7 @@ F ext/fts5/test/fts5integrity.test 62147a1e85405b986691177e0312be5a64ec9e67b1799
 F ext/fts5/test/fts5interrupt.test 09613247b273a99889808ef852898177e671406fe71fdde7ea00e78ea283d227
 F ext/fts5/test/fts5lastrowid.test be98fe3e03235296585b72daad7aed5717ba0062bae5e5c18dd6e04e194c6b28
 F ext/fts5/test/fts5leftjoin.test c0b4cafb9661379e576dc4405c0891d8fcc2782680740513c4d1fc114b43d4ad
+F ext/fts5/test/fts5limits.test 8ab67cf5d311c124b6ceb0062d0297767176df4572d955fce79fa43004dff01c
 F ext/fts5/test/fts5matchinfo.test 10c9a6f7fe61fb132299c4183c012770b10c4d5c2f2edb6df0b6607f683d737a
 F ext/fts5/test/fts5merge.test e92a8db28b45931e7a9c7b1bbd36101692759d00274df74d83fd29d25d53b3a6
 F ext/fts5/test/fts5merge2.test 3ebad1a59d6ad3fb66eff6523a09e95dc6367cbefb3cd73196801dea0425c8e2
@@ -2069,8 +2070,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 603d9ad5012ca8996783996d7b8cd6a1aabf12b21604a2ccc137f4c2d99427b9
-R 704f34dd7e7a4c385b6e7ca83d6b47da
+P 6f1a60fb7b3ef463b4131dfecbc72b7c778c794a9aa13ce4617ab6dd895508b2
+R fbd92f9aaa5b43c22e457c0cddd855bd
+T *branch * fts5-expr-limit
+T *sym-fts5-expr-limit *
+T -sym-trunk *
 U dan
-Z 03d9b3bf853393a79360eafbe071771b
+Z 944c2342cbd3e382b1cd18db6ddd42df
 # Remove this line to create a well-formed Fossil manifest.
index 59817171133bcd54186859d3da3f8e7bcd66e674..fcaf56ef4df499306cd2680e2e2ecabdbd8e3ef3 100644 (file)
@@ -1 +1 @@
-6f1a60fb7b3ef463b4131dfecbc72b7c778c794a9aa13ce4617ab6dd895508b2
\ No newline at end of file
+0e5c1ee40a146ef8b2b3c5f53d0a45e092bc8d8e933f3819805c995819d31bae
\ No newline at end of file