CVE-2019-19645: Uncontrolled Recursion (CWE-674)
value: alter.c in SQLite through 3.30.1 allows attackers to trigger infinite recursion via certain types of self-referential views in conjunction with ALTER TABLE statements.
CWE description: The product does not properly control the amount of recursion that takes place,  consuming excessive resources, such as allocated memory or the program stack.
CVSS v3 base score: 5.5
Published: 2019-12-09T16:15Z
Repository: https://github.com/sqlite/sqlite
Language: C
Files changed: manifest, manifest.uuid, src/alter.c, src/build.c, src/sqliteInt.h, test/altertab3.test
Commit date: 2019-12-09 08:13:43 +0000
Commit message: Avoid infinite recursion in the ALTER TABLE code when a view contains an unused CTE that references, directly or indirectly, the view itself.

FossilOrigin-Name: 1d2e53a39b87e364685e21de137655b6eee725e4c6d27fc90865072d7c5892b5
Diff: diff --git a/manifest b/manifest
index ff838aa5c8..39c8f9b321 100644
--- a/manifest
+++ b/manifest
@@ -1,7 +1,7 @@
-C Fix\spossible\snull\spointer\sdereferences\sin\sthe\sfts5_expr()\sscalar\sfunction.
-D 2019-12-09T02:20:37.957
+C Avoid\sinfinite\srecursion\sin\sthe\sALTER\sTABLE\scode\swhen\sa\sview\scontains\san\sunused\sCTE\sthat\sreferences,\sdirectly\sor\sindirectly,\sthe\sview\sitself.
+D 2019-12-09T08:13:43.318
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
 F Makefile.in d3a862c9742f5a08230a3b295c0a47fd3067f19356dc39935280135f90474b04
 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
@@ -459,21 +459,21 @@ F mptest/mptest.c aa41ace6dbc5050d76b02548d3521e6bbccae4f0
 F mptest/multiwrite01.test dab5c5f8f9534971efce679152c5146da265222d
 F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
 F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
 F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
-F src/alter.c c49548d04914d370864c88ed4b5e3e3b112779ba70562ebd3f191b39be151536
+F src/alter.c ef3519bcc3da6e1d8c0bda63233fb315c99f08e8cd74fe3afb9eeaa2b424bfd4
 F src/analyze.c b3ceec3fc052df8a96ca8a8c858d455dc5029ba681b4be98bb5c5a9162cfa58c
 F src/attach.c b30c44333d55a68c0a12920b5b9d40b254cbd3d4509bda77417209eeed8b3d80
 F src/auth.c a3d5bfdba83d25abed1013a8c7a5f204e2e29b0c25242a56bc02bb0c07bf1e06
 F src/backup.c f70077d40c08b7787bfe934e4d1da8030cb0cc57d46b345fba2294b7d1be23ab
 F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
 F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
 F src/btree.c d22498af716953400e314d2d98d1dac3ea5c3b01e0fd243ef9e9b132c74114ec
 F src/btree.h f27a33c49280209a93385e218306c4ee5f46ba8d7649d2f81a7166b282232484
 F src/btreeInt.h 91806f01fd1145a9a86ba3042f25c38d8faf6002701bf5e780742cf88bcff437
-F src/build.c ed6cc3e7e209d92b0ed2fa780c95b1a3aa7ecdd46a97671cfcca95079789bcc9
+F src/build.c 7c277ccc24c249b84f29ba829b0ed377e78487c93d17d999798641be0c20fad5
 F src/callback.c 88615dfc0a82167b65b452b4b305dbf86be77200b3343c6ffc6d03e92a01d181
 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
 F src/ctime.c 1b0724e66f95f33b160b1af85caaf9cceb325d22abf39bd24df4f54a73982251
 F src/date.c e1d8ac7102f3f283e63e13867acb0efa33861cf34f0faf4cdbaf9fa7a1eb7041
 F src/dbpage.c 135eb3b5e74f9ef74bde5cec2571192c90c86984fa534c88bf4a055076fa19b7
@@ -530,11 +530,11 @@ F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
 F src/select.c 0fe10579de20eb8dc04ec9ed29659fa782bee2bcc85a35734637f3e2cabc2762
 F src/shell.c.in 4a3a9e1c11847b1904f2b01d087af1c052f660902755abab457cab1756817ded
 F src/sqlite.h.in 2a23e8161775253d9cf383c2c6aa559005dc787d350dcb0be67a6c4cc3bd1d19
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 72af51aa4e912e14cd495fb6e7fac65f0940db80ed950d90911aff292cc47ce2
-F src/sqliteInt.h daef6c0ec05fdb3dd8ed1b2da6fa2fdce98917dc463a6a65b001f3be5e24ce4c
+F src/sqliteInt.h 1ac4dfde728b9d5812d1223d668d5e1f43c6fe4f469d6a7a482f298a10a03210
 F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
 F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
 F src/tclsqlite.c 8cd2600e8de23dff6cdf84d39f46ca57139b061b28f6f80b166bace17d52ab1c
 F src/test1.c c654981c1d86ebc90dd23fcc0969e6c85e28112f0acc2e2224a97a2a33e7c42f
@@ -635,11 +635,11 @@ F test/altercol.test 54374d2ba18af25bb24e23acf18a60270d4ec120b7ec0558078b59d5aa1
 F test/alterlegacy.test 82022721ce0de29cedc9a7af63bc9fcc078b0ee000f8283b4b6ea9c3eab2f44b
 F test/altermalloc.test 167a47de41b5c638f5f5c6efb59784002b196fff70f98d9b4ed3cd74a3fb80c9
 F test/altermalloc2.test fa7b1c1139ea39b8dec407cf1feb032ca8e0076bd429574969b619175ad0174b
 F test/altertab.test 4d8b79b0b88b62b90b710390df14fe99e0a3578345526886eaa550e28e3065dc
 F test/altertab2.test 8883693952f6d7fb5f754dbf1d694ed780aa883027bef04cb1fb99a3b88c9272
-F test/altertab3.test 18111c210a4992abe90247e4a83c6f6e901537288c42879d12680088612d0fa2
+F test/altertab3.test 3faf56b3f5af0dd2c12d954db4746521fc96543222da5b6e2633ed417640ce83
 F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
 F test/analyze.test 547bb700f903107b38611b014ca645d6b5bb819f5210d7bf39c40802aafeb7d7
 F test/analyze3.test 01f0b122e3e54ad2544f14f7cc7dcb4c2cb8753cad5e88c6b8d49615b3fd6a2b
 F test/analyze4.test cdf88f3f72b0f0643a1ff6c730fc5af1e42464d47478d9fbac84c333f72c014e
 F test/analyze5.test fa5131952303ac4146aba101b116b9c8cb89e2637531c334a6df7f7d19dddc0d
@@ -1850,9 +1850,9 @@ F vsixtest/pch.h 9cab7980f2ac4baa40807d8b5e52af32a21cf78c
 F vsixtest/vsixtest.sln 77cadbe4e96c1fe1bf51cd77de9e9b0a12ada547
 F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 9d75e1ccc72e9f536f45df3b24e9ecd25076cc1f7cf16b806b19e0e1b68e8326
-R 0a0e9aa93d1e91dec7ab207a55b0f9a7
+P c5d44143599f3fe98492b2b900fa3d77925c7be545096251055ceeab899a41f1
+R 22fd1a654a96a45bd7c185276aceb3b2
 U dan
-Z 52b4f37e443381d36a712c3afffc572f
+Z 7090efa7effe7e9fcfeaeecdf4e29b8c
diff --git a/manifest.uuid b/manifest.uuid
index 12ca3fd406..5ae7f863d6 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-c5d44143599f3fe98492b2b900fa3d77925c7be545096251055ceeab899a41f1
\ No newline at end of file
+1d2e53a39b87e364685e21de137655b6eee725e4c6d27fc90865072d7c5892b5
\ No newline at end of file
diff --git a/src/alter.c b/src/alter.c
index ff885e3d19..48fa527562 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -758,10 +758,11 @@ static void renameWalkWith(Walker *pWalker, Select *pSelect){
 */
 static int renameUnmapSelectCb(Walker *pWalker, Select *p){
   Parse *pParse = pWalker->pParse;
   int i;
   if( pParse->nErr ) return WRC_Abort;
+  if( p->selFlags & SF_View ) return WRC_Prune;
   if( ALWAYS(p->pEList) ){
     ExprList *pList = p->pEList;
     for(i=0; i<pList->nExpr; i++){
       if( pList->a[i].zName ){
         sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zName);
@@ -849,10 +850,11 @@ static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){
 ** This is a Walker select callback. It does nothing. It is only required
 ** because without a dummy callback, sqlite3WalkExpr() and similar do not
 ** descend into sub-select statements.
 */
 static int renameColumnSelectCb(Walker *pWalker, Select *p){
+  if( p->selFlags & SF_View ) return WRC_Prune;
   renameWalkWith(pWalker, p);
   return WRC_Continue;
 }
 
 /*
@@ -1314,12 +1316,13 @@ static void renameColumnFunc(
   sCtx.pTab = pTab;
   if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
   if( sParse.pNewTable ){
     Select *pSelect = sParse.pNewTable->pSelect;
     if( pSelect ){
+      pSelect->selFlags &= ~SF_View;
       sParse.rc = SQLITE_OK;
-      sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, 0);
+      sqlite3SelectPrep(&sParse, pSelect, 0);
       rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
       if( rc==SQLITE_OK ){
         sqlite3WalkSelect(&sWalker, pSelect);
       }
       if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
@@ -1432,10 +1435,11 @@ static int renameTableExprCb(Walker *pWalker, Expr *pExpr){
 */
 static int renameTableSelectCb(Walker *pWalker, Select *pSelect){
   int i;
   RenameCtx *p = pWalker->u.pRename;
   SrcList *pSrc = pSelect->pSrc;
+  if( pSelect->selFlags & SF_View ) return WRC_Prune;
   if( pSrc==0 ){
     assert( pWalker->pParse->db->mallocFailed );
     return WRC_Abort;
   }
   for(i=0; i<pSrc->nSrc; i++){
@@ -1511,14 +1515,17 @@ static void renameTableFunc(
       if( sParse.pNewTable ){
         Table *pTab = sParse.pNewTable;
 
         if( pTab->pSelect ){
           if( isLegacy==0 ){
+            Select *pSelect = pTab->pSelect;
             NameContext sNC;
             memset(&sNC, 0, sizeof(sNC));
             sNC.pParse = &sParse;
 
+            assert( pSelect->selFlags & SF_View );
+            pSelect->selFlags &= ~SF_View;
             sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC);
             if( sParse.nErr ) rc = sParse.rc;
             sqlite3WalkSelect(&sWalker, pTab->pSelect);
           }
         }else{
diff --git a/src/build.c b/src/build.c
index 46bf3677e1..171cc68cf4 100644
--- a/src/build.c
+++ b/src/build.c
@@ -2483,10 +2483,11 @@ void sqlite3CreateView(
   /* Make a copy of the entire SELECT statement that defines the view.
   ** This will force all the Expr.token.z values to be dynamically
   ** allocated rather than point to the input string - which means that
   ** they will persist after the current sqlite3_exec() call returns.
   */
+  pSelect->selFlags |= SF_View;
   if( IN_RENAME_OBJECT ){
     p->pSelect = pSelect;
     pSelect = 0;
   }else{
     p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index b870bffcfb..2aca2abfa9 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -2954,10 +2954,11 @@ struct Select {
 #define SF_Converted     0x0010000 /* By convertCompoundSelectToSubquery() */
 #define SF_IncludeHidden 0x0020000 /* Include hidden columns in output */
 #define SF_ComplexResult 0x0040000 /* Result contains subquery or function */
 #define SF_WhereBegin    0x0080000 /* Really a WhereBegin() call.  Debug Only */
 #define SF_WinRewrite    0x0100000 /* Window function rewrite accomplished */
+#define SF_View          0x0200000 /* SELECT statement is a view */
 
 /*
 ** The results of a SELECT can be distributed in several ways, as defined
 ** by one of the following macros.  The "SRT" prefix means "SELECT Result
 ** Type".
diff --git a/test/altertab3.test b/test/altertab3.test
index d684884bca..7103a810b5 100644
--- a/test/altertab3.test
+++ b/test/altertab3.test
@@ -485,7 +485,38 @@ do_execsql_test 21.1 {
   ) IN() ; 
   CREATE TABLE a(a);
   ALTER TABLE a RENAME a TO b;
 }
 
+#------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 22.1 {
+  CREATE TABLE t1(a);
+  CREATE VIEW v2(b) AS SELECT * FROM v2;
+}
+
+do_catchsql_test 22.2 {
+  ALTER TABLE t1 RENAME TO t4;
+} {1 {error in view v2: view v2 is circularly defined}}
+
+do_execsql_test 22.3 {
+  DROP VIEW v2;
+  CREATE VIEW v2(b) AS WITH t3 AS (SELECT b FROM v2) SELECT * FROM t3;
+}
+
+breakpoint
+do_catchsql_test 22.4 {
+  ALTER TABLE t1 RENAME TO t4;
+} {1 {error in view v2: view v2 is circularly defined}}
+
+do_execsql_test 22.5 {
+  DROP VIEW v2;
+  CREATE VIEW v2(b) AS WITH t3 AS (SELECT b FROM v2) VALUES(1);
+}
+
+do_catchsql_test 22.6 {
+  ALTER TABLE t1 RENAME TO t4;
+} {0 {}}
+
 
 finish_test

Vulnerable code: C Fix\spossible\snull\spointer\sdereferences\sin\sthe\sfts5_expr()\sscalar\sfunction.
D 2019-12-09T02:20:37.957
F src/alter.c c49548d04914d370864c88ed4b5e3e3b112779ba70562ebd3f191b39be151536
F src/build.c ed6cc3e7e209d92b0ed2fa780c95b1a3aa7ecdd46a97671cfcca95079789bcc9
F src/sqliteInt.h daef6c0ec05fdb3dd8ed1b2da6fa2fdce98917dc463a6a65b001f3be5e24ce4c
F test/altertab3.test 18111c210a4992abe90247e4a83c6f6e901537288c42879d12680088612d0fa2
P 9d75e1ccc72e9f536f45df3b24e9ecd25076cc1f7cf16b806b19e0e1b68e8326
R 0a0e9aa93d1e91dec7ab207a55b0f9a7
Z 52b4f37e443381d36a712c3afffc572f
c5d44143599f3fe98492b2b900fa3d77925c7be545096251055ceeab899a41f1
      sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, 0);
Fixed code: C Avoid\sinfinite\srecursion\sin\sthe\sALTER\sTABLE\scode\swhen\sa\sview\scontains\san\sunused\sCTE\sthat\sreferences,\sdirectly\sor\sindirectly,\sthe\sview\sitself.
D 2019-12-09T08:13:43.318
F src/alter.c ef3519bcc3da6e1d8c0bda63233fb315c99f08e8cd74fe3afb9eeaa2b424bfd4
F src/build.c 7c277ccc24c249b84f29ba829b0ed377e78487c93d17d999798641be0c20fad5
F src/sqliteInt.h 1ac4dfde728b9d5812d1223d668d5e1f43c6fe4f469d6a7a482f298a10a03210
F test/altertab3.test 3faf56b3f5af0dd2c12d954db4746521fc96543222da5b6e2633ed417640ce83
P c5d44143599f3fe98492b2b900fa3d77925c7be545096251055ceeab899a41f1
R 22fd1a654a96a45bd7c185276aceb3b2
Z 7090efa7effe7e9fcfeaeecdf4e29b8c
1d2e53a39b87e364685e21de137655b6eee725e4c6d27fc90865072d7c5892b5
  if( p->selFlags & SF_View ) return WRC_Prune;
  if( p->selFlags & SF_View ) return WRC_Prune;
      pSelect->selFlags &= ~SF_View;
      sqlite3SelectPrep(&sParse, pSelect, 0);
  if( pSelect->selFlags & SF_View ) return WRC_Prune;
            Select *pSelect = pTab->pSelect;
            assert( pSelect->selFlags & SF_View );
            pSelect->selFlags &= ~SF_View;
  pSelect->selFlags |= SF_View;
#define SF_View          0x0200000 /* SELECT statement is a view */
#------------------------------------------------------------------------
#
reset_db
do_execsql_test 22.1 {
  CREATE TABLE t1(a);
  CREATE VIEW v2(b) AS SELECT * FROM v2;
}

do_catchsql_test 22.2 {
  ALTER TABLE t1 RENAME TO t4;
} {1 {error in view v2: view v2 is circularly defined}}

do_execsql_test 22.3 {
  DROP VIEW v2;
  CREATE VIEW v2(b) AS WITH t3 AS (SELECT b FROM v2) SELECT * FROM t3;
}

breakpoint
do_catchsql_test 22.4 {
  ALTER TABLE t1 RENAME TO t4;
} {1 {error in view v2: view v2 is circularly defined}}

do_execsql_test 22.5 {
  DROP VIEW v2;
  CREATE VIEW v2(b) AS WITH t3 AS (SELECT b FROM v2) VALUES(1);
}

do_catchsql_test 22.6 {
  ALTER TABLE t1 RENAME TO t4;
} {0 {}}