1
0
mirror of https://github.com/sqlite/sqlite.git synced 2026-10-23 21:49:02 +03:00

Add the SQLITE_PREPARE_FROM_DDL flag to sqlite3_prepare_v3(). Use this to prevent fts3/4 tables in non-trusted schemas from calling unsafe SQL functions.

FossilOrigin-Name: 26f39ac806a5582fab1497e38a4ce68dad2b79df8c8aade43c4e21eed7576931
This commit is contained in:
dan
2025-12-22 15:22:02 +00:00
parent 8d41b94bb4
commit add73c831b
12 changed files with 171 additions and 33 deletions

View File

@@ -1816,9 +1816,7 @@ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){
zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
if( !zSql ) return SQLITE_NOMEM;
p->bLock++;
rc = sqlite3_prepare_v3(
p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
);
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt);
p->bLock--;
sqlite3_free(zSql);
}
@@ -3393,9 +3391,7 @@ static int fts3FilterMethod(
}
if( zSql ){
p->bLock++;
rc = sqlite3_prepare_v3(
p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
);
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt);
p->bLock--;
sqlite3_free(zSql);
}else{
@@ -4018,6 +4014,7 @@ static int fts3IntegrityMethod(
UNUSED_PARAMETER(isQuick);
rc = sqlite3Fts3IntegrityCheck(p, &bOk);
assert( pVtab->zErrMsg==0 || rc!=SQLITE_OK );
assert( rc!=SQLITE_CORRUPT_VTAB );
if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"

View File

@@ -601,6 +601,15 @@ int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
(*(u8*)(p)&0x80) ? sqlite3Fts3GetVarint32(p, piVal) : (*piVal=*(u8*)(p), 1) \
)
int sqlite3Fts3PrepareStmt(
Fts3Table *p, /* Prepare for this connection */
const char *zSql, /* SQL to prepare */
int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */
int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */
sqlite3_stmt **pp /* OUT: Prepared statement */
);
/* fts3.c */
void sqlite3Fts3ErrMsg(char**,const char*,...);
int sqlite3Fts3PutVarint(char *, sqlite3_int64);

View File

@@ -273,6 +273,24 @@ struct SegmentNode {
#define SQL_UPDATE_LEVEL_IDX 38
#define SQL_UPDATE_LEVEL 39
/*
** Wrapper around sqlite3_prepare_v3() to ensure that SQLITE_PREPARE_FROM_DDL
** is always set.
*/
int sqlite3Fts3PrepareStmt(
Fts3Table *p, /* Prepare for this connection */
const char *zSql, /* SQL to prepare */
int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */
int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */
sqlite3_stmt **pp /* OUT: Prepared statement */
){
int f = SQLITE_PREPARE_FROM_DDL
|((bAllowVtab==0) ? SQLITE_PREPARE_NO_VTAB : 0)
|(bPersist ? SQLITE_PREPARE_PERSISTENT : 0);
return sqlite3_prepare_v3(p->db, zSql, -1, f, pp, NULL);
}
/*
** This function is used to obtain an SQLite prepared statement handle
** for the statement identified by the second argument. If successful,
@@ -398,12 +416,12 @@ static int fts3SqlStmt(
pStmt = p->aStmt[eStmt];
if( !pStmt ){
int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB;
int bAllowVtab = 0;
char *zSql;
if( eStmt==SQL_CONTENT_INSERT ){
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
}else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
f &= ~SQLITE_PREPARE_NO_VTAB;
bAllowVtab = 1;
zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist);
}else{
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
@@ -411,7 +429,7 @@ static int fts3SqlStmt(
if( !zSql ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_prepare_v3(p->db, zSql, -1, f, &pStmt, NULL);
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, bAllowVtab, &pStmt);
sqlite3_free(zSql);
assert( rc==SQLITE_OK || pStmt==0 );
p->aStmt[eStmt] = pStmt;
@@ -3578,7 +3596,7 @@ static int fts3DoRebuild(Fts3Table *p){
if( !zSql ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt);
sqlite3_free(zSql);
}
@@ -5331,7 +5349,7 @@ int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
if( !zSql ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt);
sqlite3_free(zSql);
}

View File

@@ -1,5 +1,5 @@
C Fix\sspurious\serror\slog\sreports\scaused\sby\s[d25c8a6222d4e3f2].\s\sSee\n[forum:/forumpost/d2326687662430c1|forum\spost\sd2326687662].
D 2025-12-22T12:33:27.145
C Add\sthe\sSQLITE_PREPARE_FROM_DDL\sflag\sto\ssqlite3_prepare_v3().\sUse\sthis\sto\sprevent\sfts3/4\stables\sin\snon-trusted\sschemas\sfrom\scalling\sunsafe\sSQL\sfunctions.
D 2025-12-22T15:22:02.051
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -79,9 +79,9 @@ F ext/fts3/README.content b9078d0843a094d86af0d48dffbff13c906702b4c3558012e67b9c
F ext/fts3/README.syntax b72477722e9b4fe43f8403227d790a1c94221bfad15c27863a4b36d1052e892b
F ext/fts3/README.tokenizers b92bdeb8b46503f0dd301d364efc5ef59ef9fa8e2758b8e742f39fa93a2e422d
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
F ext/fts3/fts3.c 4f02858ab845a97bedf06e6cc1fba49a81fe5e00a26df68d0ad0f00a5814fa70
F ext/fts3/fts3.c 6cc7bbc307f27e7b6ee2e1d5ff63ffff4df3b42529dfe00eb34ddded417961b3
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
F ext/fts3/fts3Int.h ed9b8bc5ed5be402069651e49d4855cb849af706cf3fe68548f58a2c21eefc7f
F ext/fts3/fts3Int.h fd6051f7aa4db93e05fdc703ef35faf79f78170419e809139109d7aef28f4170
F ext/fts3/fts3_aux.c 7eab82a9cf0830f6551ba3abfdbe73ed39e322a4d3940ee82fbf723674ecd9f3
F ext/fts3/fts3_expr.c 5c13796638d8192c388777166075cdc8bc4b6712024cd5b72c31acdbefce5984
F ext/fts3/fts3_hash.c d9dba473741445789330c7513d4f65737c92df23c3212784312931641814672a
@@ -97,7 +97,7 @@ F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3
F ext/fts3/fts3_tokenizer1.c c1de4ae28356ad98ccb8b2e3388a7fdcce7607b5523738c9afb6275dab765154
F ext/fts3/fts3_unicode.c de426ff05c1c2e7bce161cf6b706638419c3a1d9c2667de9cb9dc0458c18e226
F ext/fts3/fts3_unicode2.c 416eb7e1e81142703520d284b768ca2751d40e31fa912cae24ba74860532bf0f
F ext/fts3/fts3_write.c 3a412c62e39c72283e34a51ec60c59eee6d684882fea7156b09a9733bd1f10e8
F ext/fts3/fts3_write.c d218b687fb55bce8c9340c6dbb368a10d94647cbe39801d85492d576a4e7da75
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/tool/fts3cov.sh c331d006359456cf6f8f953e37f2b9c7d568f3863f00bb5f7eb87fea4ac01b73
F ext/fts3/tool/fts3view.c 413c346399159df81f86c4928b7c4a455caab73bfbc8cd68f950f632e5751674
@@ -686,8 +686,8 @@ F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/date.c e19e0cfff9a41bfdd884c655755f6f00bca4c1a22272b56e0dd6667b7ea893a2
F src/dbpage.c c9ea81c11727f27e02874611e92773e68e2a90a875ef2404b084564c235fd91f
F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c
F src/delete.c 03a77ba20e54f0f42ebd8eddf15411ed6bdb06a2c472ac4b6b336521bf7cea42
F src/expr.c b4530f3e23d8ea38c7de4c7b55af52d43156c79128c042a580e670776f59a9bc
F src/delete.c e020dde34838369e2f0eff75f25c44a4e56a41262593f7c48d1223689d674e4d
F src/expr.c 252e62742f5bb01517377c93057b6040ab954034ec3dde4d6fc583565d859a9c
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c c065da737307a29e4d240ac727758dbf4102cb3218a1f651eb689b6a6fa12531
F src/func.c 0b802107498048d3dcac0b757720bcb8506507ce02159e213ab8161458eb293b
@@ -736,9 +736,9 @@ F src/printf.c b1b29b5e58e1530d5daeee5963d3c318d8ab2d7e38437580e28755753e0c1ded
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c 47aa7fdc9ec4c19b103ac5e79d7887d30119b5675309facf5eed1118391c868b
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c 9d55dc7bfea75b7aec14e819b9f65ca97472cea301ac5115d45ad435a63f7350
F src/select.c 85852256d860f3ba5be4a9edc1238e68dbea082a0167f31b7345c821ae45775d
F src/shell.c.in c4b775c664c339ac0351549a998b5f8816bf2496af5385e3937050c1fb5688fe
F src/sqlite.h.in b7d0e99d1384e73882f3157d86e0cd886d0c510d8db2b288b1d17631d6f26089
F src/sqlite.h.in ecdd8ee84fe9e30d51433a4cc7951bb0423f588943f5d6f32c6c6ea779662f90
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 5d5330f5f8461f5ce74960436ddcfa53ecd09c2b8b23901e22ae38aec3243998
F src/sqliteInt.h af67bc95fa6b66cd3c7f3d18d2d040ad386e4cbb02965ee318cc721ee9d5fa45
@@ -804,7 +804,7 @@ F src/utf.c 7267c3fb9e2467020507601af3354c2446c61f444387e094c779dccd5ca62165
F src/util.c 36fb1150062957280777655976f3f9a75db236cb8207a0770ceae8d5ec17fcd3
F src/vacuum.c 1bacdd0a81d2b5dc1c508fbf0d938c89fa78dd8d5b46ec92686d44030d4f4789
F src/vdbe.c b44c366e83412d3b8c190feb1f029b7d02e1bd69252a57b32f195107f0d03964
F src/vdbe.h be33bd7b17f2ec92939642416030491508c51071f6c14e27cd195983fec56b63
F src/vdbe.h 966d0677a540b7ea6549b7c4e1312fc0d830fce3a235a58c801f2cc31cf5ecf9
F src/vdbeInt.h 2aaeb6df2938b181b4700a9328688a3986f2bba71e8b96f6a80671316618fa49
F src/vdbeapi.c 6a2181cfd27c86b4cc1d8abb27ae11f3b3f0357567814fa276ec37b043542938
F src/vdbeaux.c 908d8a191aed444b2e4c920159249127f3ff67b94c56a16fad1dfdf9c7488f20
@@ -1153,7 +1153,7 @@ F test/fts3aux1.test 1880eaa75c586cd10f53080479a2b819b3915ae7ce55c4e0ba8f1fe05ac
F test/fts3aux2.test 2459e7fa3e22734aed237d1e2ae192f5541c4d8b218956ad2d90754977bf907f
F test/fts3b.test c15c4a9d04e210d0be67e54ce6a87b927168fbf9c1e3faec8c1a732c366fd491
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
F test/fts3comp1.test f1b05ece56481f3973ece6a968cbabeeb0866c43f5375318313c003518ac53fb
F test/fts3conf.test c9cd45433b6787d48a43e84949aa2eb8b3b3d242bac7276731c1476290d31f29
F test/fts3corrupt.test 6732477c5ace050c5758a40a8b5706c8c0cccd416b9c558e0e15224805a40e57
F test/fts3corrupt2.test e318f0676e5e78d5a4b702637e2bb25265954c08a1b1e4aaf93c7880bb0c67d0
@@ -1203,7 +1203,7 @@ F test/fts3tok_err.test 52273cd193b9036282f7bacb43da78c6be87418d
F test/fts3varint.test 0b84a3fd4eba8a39f3687523804d18f3b322e6d4539a55bf342079c3614f2ada
F test/fts4aa.test 0e6bfd6a81695a39b23e448dda25d864e63dda75bde6949c45ddc95426c6c3f5
F test/fts4check.test f0ea5e5581951d8ef7a341eea14486daf6c5f516a2f3273b0d5e8cb8a6cd3bd2
F test/fts4content.test 73bbb123420d2c46ef2fb3b24761e9acdb78b0877179d3a5d7d57aada08066f6
F test/fts4content.test 7f441866207f5b1e76e0f18bde5d9925d1ee8f60388054613dd14a29a79f0bc4
F test/fts4docid.test e33c383cfbdff0284685604d256f347a18fdbf01
F test/fts4growth.test 289833c34ad45a5e6e6133b53b6a71647231fb89d36ddcb8d9c87211b6721d7f
F test/fts4growth2.test 13ad4e76451af6e6906c95cdc725d01b00044269
@@ -2187,8 +2187,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P f43d3339c1f06aabcb8e678ed4b91a490cfd0bc880c642ce9c1138c7e08cb360
R 368416e9322dccc113e01f13b887d649
U drh
Z 991a818d717546a1f42472b805bde58a
P 403dafc7790a2e8d1772537800760addc47d3e3555bda343c3991d56c1b36e70
R 7581ed8e32b81aa2384ce190ebdf96b2
U dan
Z 1915d46eae49cba5052e06a07b4e8b93
# Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
403dafc7790a2e8d1772537800760addc47d3e3555bda343c3991d56c1b36e70
26f39ac806a5582fab1497e38a4ce68dad2b79df8c8aade43c4e21eed7576931

View File

@@ -86,7 +86,7 @@ static int vtabIsReadOnly(Parse *pParse, Table *pTab){
** * Only allow DELETE, INSERT, or UPDATE of non-SQLITE_VTAB_INNOCUOUS
** virtual tables if PRAGMA trusted_schema=ON.
*/
if( pParse->pToplevel!=0
if( (pParse->pToplevel!=0 || (pParse->prepFlags & SQLITE_PREPARE_FROM_DDL))
&& pTab->u.vtab.p->eVtabRisk >
((pParse->db->flags & SQLITE_TrustedSchema)!=0)
){

View File

@@ -1280,7 +1280,9 @@ void sqlite3ExprFunctionUsable(
){
assert( !IN_RENAME_OBJECT );
assert( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 );
if( ExprHasProperty(pExpr, EP_FromDDL) ){
if( ExprHasProperty(pExpr, EP_FromDDL)
|| pParse->prepFlags & SQLITE_PREPARE_FROM_DDL
){
if( (pDef->funcFlags & SQLITE_FUNC_DIRECT)!=0
|| (pParse->db->flags & SQLITE_TrustedSchema)==0
){

View File

@@ -6181,7 +6181,7 @@ static int selectExpander(Walker *pWalker, Select *p){
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( ALWAYS(IsVirtual(pTab))
&& pFrom->fg.fromDDL
&& (pFrom->fg.fromDDL || (pParse->prepFlags & SQLITE_PREPARE_FROM_DDL))
&& ALWAYS(pTab->u.vtab.p!=0)
&& pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0)
){

View File

@@ -4433,12 +4433,20 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
** fails, the sqlite3_prepare_v3() call returns the same error indications
** with or without this flag; it just omits the call to [sqlite3_log()] that
** logs the error.
**
** [[SQLITE_PREPARE_FROM_DDL]] <dt>SQLITE_PREPARE_FROM_DDL</dt>
** <dd>The SQLITE_PREPARE_FROM_DDL causes the SQL compiler to behave as if
** the SQL statement was part of a database schema. This only makes a
** difference if the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is set to off.
** This flag may be used by virtual table implementations that do not
** completely control the SQL commands that they evaluate.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
#define SQLITE_PREPARE_DONT_LOG 0x10
#define SQLITE_PREPARE_FROM_DDL 0x20
/*
** CAPI3REF: Compiling An SQL Statement

View File

@@ -186,7 +186,7 @@ typedef struct VdbeOpList VdbeOpList;
** Additional non-public SQLITE_PREPARE_* flags
*/
#define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */
#define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */
#define SQLITE_PREPARE_MASK 0x3f /* Mask of public flags */
/*
** Prototypes for the VDBE interface. See comments on the implementation

View File

@@ -112,4 +112,51 @@ do_catchsql_test 2.2 {
CREATE VIRTUAL TABLE t2 USING fts4(x, uncompress=unzip)
} {1 {missing compress parameter in fts4 constructor}}
#--------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
PRAGMA trusted_schema = OFF;
}
set ::myfunc_invoked 0
proc myfunc {data} {
incr ::myfunc_invoked
return $data
}
db func myfunc myfunc
do_execsql_test 3.1 {
CREATE VIEW v1 AS SELECT myfunc('xyz');
}
do_catchsql_test 3.2 {
SELECT * FROM v1
} {1 {unsafe use of myfunc()}}
do_execsql_test 3.3 {
CREATE VIRTUAL TABLE f1 USING fts4(x, compress=myfunc, uncompress=myfunc);
}
do_catchsql_test 3.4 {
INSERT INTO f1(rowid, x) VALUES(123, 'one two three');
} {1 {SQL logic error}}
do_test 3.5 {
set ::myfunc_invoked
} {0}
do_execsql_test 3.6.1 {
CREATE TABLE t1(x);
CREATE TABLE t2(y);
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
INSERT INTO t2 VALUES( myfunc(new.x) );
END;
}
do_catchsql_test 3.6.2 {
INSERT INTO t1 VALUES('hello world');
} {1 {unsafe use of myfunc()}}
finish_test

View File

@@ -638,7 +638,6 @@ do_catchsql_test 11.1 {
# Check that an fts4 table cannot be its own content table.
#
reset_db
breakpoint
do_execsql_test 12.1.1 {
CREATE VIRTUAL TABLE t1 USING fts4(a, content=t1 );
INSERT INTO t1(rowid, a) VALUES(1, 'abc');
@@ -669,6 +668,64 @@ do_catchsql_test 12.2.4 {
SELECT count(*) FROM t1;
} {1 {SQL logic error}}
#---------------------------------------------------------------------------
# Check that an fts4 table cannot read from an unsafe vtab in a non-trusted
# schema.
reset_db
do_execsql_test 13.0 {
PRAGMA trusted_schema = off;
CREATE VIRTUAL TABLE t1 USING fts4(data, content=sqlite_dbpage);
}
do_catchsql_test 13.1 {
INSERT INTO t1(t1) VALUES('rebuild');
} {1 {SQL logic error}}
proc vtab_command {method args} {
switch -- $method {
xConnect {
return "CREATE TABLE t1(a)"
}
xBestIndex {
return ""
}
xFilter {
return [list sql {SELECT 1, 123}]
}
xUpdate {
return 123
}
}
return {}
}
register_tcl_module db xyz
do_execsql_test 13.2.0 {
CREATE VIRTUAL TABLE aa USING tcl(vtab_command);
}
do_execsql_test 13.2.1 {
INSERT INTO aa VALUES('one two three');
}
do_test 13.2.2 {
set ::stmt [sqlite3_prepare_v3 db \
"INSERT INTO aa VALUES('one two three');" -1 0x00
]
sqlite3_finalize $::stmt
} {SQLITE_OK}
do_test 13.2.2 {
list [catch {
set ::stmt [sqlite3_prepare_v3 db \
"INSERT INTO aa VALUES('one two three');" -1 0x20
]
} msg] $msg
} {1 {(1) unsafe use of virtual table "aa"}}
finish_test