Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -3110,11 +3110,11 @@ freeTempSpace(pBt); rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, pageSize-usableSize); return rc; } - if( (pBt->db->flags & SQLITE_WriteSchema)==0 && nPage>nPageFile ){ + if( sqlite3WritableSchema(pBt->db)==0 && nPage>nPageFile ){ rc = SQLITE_CORRUPT_BKPT; goto page1_init_failed; } /* EVIDENCE-OF: R-28312-64704 However, the usable size is not allowed to ** be less than 480. In other words, if the page size is 512, then the Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -794,10 +794,24 @@ iDb = db->init.iDb; *pUnqual = pName1; } return iDb; } + +/* +** True if PRAGMA writable_schema is ON +*/ +int sqlite3WritableSchema(sqlite3 *db){ + testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==0 ); + testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))== + SQLITE_WriteSchema ); + testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))== + SQLITE_Defensive ); + testcase( (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))== + (SQLITE_WriteSchema|SQLITE_Defensive) ); + return (db->flags&(SQLITE_WriteSchema|SQLITE_Defensive))==SQLITE_WriteSchema; +} /* ** 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 ** trigger). All names are legal except those that begin with the string @@ -804,11 +818,11 @@ ** "sqlite_" (in upper, lower or mixed case). This portion of the namespace ** is reserved for internal use. */ int sqlite3CheckObjectName(Parse *pParse, const char *zName){ if( !pParse->db->init.busy && pParse->nested==0 - && (pParse->db->flags & SQLITE_WriteSchema)==0 + && sqlite3WritableSchema(pParse->db)==0 && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName); return SQLITE_ERROR; } return SQLITE_OK; Index: src/dbpage.c ================================================================== --- src/dbpage.c +++ src/dbpage.c @@ -311,10 +311,14 @@ int iDb; Btree *pBt; Pager *pPager; int szPage; + if( pTab->db->flags & SQLITE_Defensive ){ + zErr = "read-only"; + goto update_fail; + } if( argc==1 ){ zErr = "cannot delete"; goto update_fail; } pgno = sqlite3_value_int(argv[0]); Index: src/delete.c ================================================================== --- src/delete.c +++ src/delete.c @@ -61,12 +61,12 @@ ** In either case leave an error message in pParse and return non-zero. */ if( ( IsVirtual(pTab) && sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ) || ( (pTab->tabFlags & TF_Readonly)!=0 - && (pParse->db->flags & SQLITE_WriteSchema)==0 - && pParse->nested==0 ) + && sqlite3WritableSchema(pParse->db)==0 + && pParse->nested==0) ){ sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName); return 1; } Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -833,10 +833,11 @@ { SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension }, { SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, SQLITE_NoCkptOnClose }, { SQLITE_DBCONFIG_ENABLE_QPSG, SQLITE_EnableQPSG }, { SQLITE_DBCONFIG_TRIGGER_EQP, SQLITE_TriggerEQP }, { SQLITE_DBCONFIG_RESET_DATABASE, SQLITE_ResetDatabase }, + { SQLITE_DBCONFIG_DEFENSIVE, SQLITE_Defensive }, }; unsigned int i; rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ for(i=0; i1 && strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue; Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -2156,10 +2156,33 @@ **
  • sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); ** ** Because resetting a database is destructive and irreversible, the ** process requires the use of this obscure API and multiple steps to help ** ensure that it does not happen by accident. +** +**
    SQLITE_DBCONFIG_DEFENSIVE
    +**
    The SQLITE_DBCONFIG_DEFENSIVE option actives or deactivates the +** "defensive" flag for a database connection. When the defensive +** flag is enabled, some obscure features of SQLite are disabled in order +** to reduce the attack surface. Applications that run untrusted SQL +** can activate this flag to reduce the risk of zero-day exploits. +**

    +** Features disabled by the defensive flag include: +**

      +**
    • The [PRAGMA writable_schema=ON] statement. +**
    • Writes to the [sqlite_dbpage] virtual table. +**
    +** New restrictions may be added in future releases. +**

    +** To be clear: It should never be possible for hostile SQL to cause +** arbitrary memory reads, memory leaks, buffer overflows, assertion +** faults, arbitrary code execution, crashes, or other mischief, regardless +** of the value of the defensive flag. Any occurrance of these problems +** is considered a serious bug and will be fixed promptly. It is not +** necessary to enable the defensive flag in order to make SQLite secure +** against attack. The defensive flag merely provides an additional layer +** of defense against unknown vulnerabilities. **

    ** */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ @@ -2169,11 +2192,12 @@ #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ #define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */ #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ #define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1009 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes ** METHOD: sqlite3 ** Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -1538,10 +1538,11 @@ #define SQLITE_EnableQPSG 0x00800000 /* Query Planner Stability Guarantee*/ #define SQLITE_TriggerEQP 0x01000000 /* Show trigger EXPLAIN QUERY PLAN */ #define SQLITE_ResetDatabase 0x02000000 /* Reset the database */ #define SQLITE_LegacyAlter 0x04000000 /* Legacy ALTER TABLE behaviour */ #define SQLITE_NoSchemaError 0x08000000 /* Do not report schema parse errors*/ +#define SQLITE_Defensive 0x10000000 /* Input SQL is likely hostile */ /* Flags used only if debugging */ #define HI(X) ((u64)(X)<<32) #ifdef SQLITE_DEBUG #define SQLITE_SqlTrace HI(0x0001) /* Debug print SQL as it executes */ @@ -4201,10 +4202,11 @@ int sqlite3ExprCollSeqMatch(Parse*,Expr*,Expr*); Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int); Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*); Expr *sqlite3ExprSkipCollate(Expr*); int sqlite3CheckCollSeq(Parse *, CollSeq *); +int sqlite3WritableSchema(sqlite3*); int sqlite3CheckObjectName(Parse *, const char *); void sqlite3VdbeSetChanges(sqlite3 *, int); int sqlite3AddInt64(i64*,i64); int sqlite3SubInt64(i64*,i64); int sqlite3MulInt64(i64*,i64); Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -7554,10 +7554,11 @@ { "LOAD_EXTENSION", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION }, { "NO_CKPT_ON_CLOSE",SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE }, { "QPSG", SQLITE_DBCONFIG_ENABLE_QPSG }, { "TRIGGER_EQP", SQLITE_DBCONFIG_TRIGGER_EQP }, { "RESET_DB", SQLITE_DBCONFIG_RESET_DATABASE }, + { "DEFENSIVE", SQLITE_DBCONFIG_DEFENSIVE }, }; int i; int v; const char *zSetting; sqlite3 *db; Index: test/index.test ================================================================== --- test/index.test +++ test/index.test @@ -623,10 +623,17 @@ do_test index-18.1 { catchsql { CREATE TABLE sqlite_t1(a, b, c); } } {1 {object name reserved for internal use: sqlite_t1}} +do_test index-18.1.2 { + sqlite3_db_config db DEFENSIVE 1 + catchsql { + CREATE TABLE sqlite_t1(a, b, c); + } +} {1 {object name reserved for internal use: sqlite_t1}} +sqlite3_db_config db DEFENSIVE 0 do_test index-18.2 { catchsql { CREATE INDEX sqlite_i1 ON t7(c); } } {1 {object name reserved for internal use: sqlite_i1}}