Subject: [PATCH] Do not allow CREATE TABLE or CREATE VIEW of an object with a name that looks like a shadow table name. diff --git a/src/build.c b/src/build.c index 3412670..f273394 100644 --- a/src/build.c +++ b/src/build.c @@ -814,6 +814,22 @@ int sqlite3WritableSchema(sqlite3 *db){ return (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==SQLITE_WriteSchema; } +/* +** Return TRUE if shadow tables should be read-only in the current +** context. +*/ +int sqlite3ReadOnlyShadowTables(sqlite3 *db){ +#ifndef SQLITE_OMIT_VIRTUALTABLE + if( (db->flags & SQLITE_Defensive)!=0 + && db->pVtabCtx==0 + && db->nVdbeExec==0 + ){ + return 1; + } +#endif + return 0; +} + /* ** This routine is used to check if the UTF-8 string zName is a legal ** unqualified name for a new schema object (table, index, view or @@ -822,9 +838,10 @@ int sqlite3WritableSchema(sqlite3 *db){ ** is reserved for internal use. */ int sqlite3CheckObjectName(Parse *pParse, const char *zName){ - if( !pParse->db->init.busy && pParse->nested==0 + if(( !pParse->db->init.busy && pParse->nested==0 && sqlite3WritableSchema(pParse->db)==0 - && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ + && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ) || + (sqlite3ReadOnlyShadowTables(pParse->db) && sqlite3ShadowTableName(pParse->db, zName))){ sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName); return SQLITE_ERROR; } @@ -1929,7 +1946,7 @@ int sqlite3IsShadowTableOf(sqlite3 *db, Table *pTab, const char *zName){ ** zName is temporarily modified while this routine is running, but is ** restored to its original value prior to this routine returning. */ -static int isShadowTableName(sqlite3 *db, char *zName){ +int sqlite3ShadowTableName(sqlite3 *db, const char *zName){ char *zTail; /* Pointer to the last "_" in zName */ Table *pTab; /* Table that zName is a shadow of */ @@ -1942,8 +1959,6 @@ static int isShadowTableName(sqlite3 *db, char *zName){ if( !IsVirtual(pTab) ) return 0; return sqlite3IsShadowTableOf(db, pTab, zName); } -#else -# define isShadowTableName(x,y) 0 #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ /* @@ -1985,7 +2000,7 @@ void sqlite3EndTable( p = pParse->pNewTable; if( p==0 ) return; - if( pSelect==0 && isShadowTableName(db, p->zName) ){ + if( pSelect==0 && sqlite3ShadowTableName(db, p->zName) ){ p->tabFlags |= TF_Shadow; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 60b2ebd..e5ba8a0 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4408,6 +4408,11 @@ void sqlite3AutoLoadExtensions(sqlite3*); ); # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) #endif +#ifndef SQLITE_OMIT_VIRTUALTABLE + int sqlite3ShadowTableName(sqlite3 *db, const char *zName); +#else +# define sqlite3ShadowTableName(A,B) 0 +#endif #ifndef SQLITE_OMIT_VIRTUALTABLE int sqlite3IsShadowTableOf(sqlite3*,Table*,const char*); #else diff --git a/test/altertab.test b/test/altertab.test index 891b081..0705abc 100644 --- a/test/altertab.test +++ b/test/altertab.test @@ -547,13 +547,29 @@ ifcapable fts3 { } {1 {table y1_segments may not be modified}} do_catchsql_test 16.20 { - ALTER TABLE y1_segments RENAME TO abc; - } {1 {table y1_segments may not be altered}} - - do_catchsql_test 16.21 { DROP TABLE y1_segments; } {1 {table y1_segments may not be dropped}} + do_catchsql_test 16.20 { + ALTER TABLE y1_segments RENAME TO abc; + } {1 {table y1_segments may not be altered}} + sqlite3_db_config db DEFENSIVE 0 + do_catchsql_test 16.22 { + ALTER TABLE y1_segments RENAME TO abc; + } {0 {}} + sqlite3_db_config db DEFENSIVE 1 + do_catchsql_test 16.23 { + CREATE TABLE y1_segments AS SELECT * FROM abc; + } {1 {object name reserved for internal use: y1_segments}} + do_catchsql_test 16.24 { + CREATE VIEW y1_segments AS SELECT * FROM abc; + } {1 {object name reserved for internal use: y1_segments}} + sqlite3_db_config db DEFENSIVE 0 + do_catchsql_test 16.25 { + ALTER TABLE abc RENAME TO y1_segments; + } {0 {}} + sqlite3_db_config db DEFENSIVE 1 + do_execsql_test 16.30 { ALTER TABLE y1 RENAME TO z1; }