-C Fix\sthe\ssqlite3ParseUri()\sfor\simproved\sdefenses\sagainst\sridiculously\slong\nURI\sinputs\scausing\sinteger\soverflow\sproblems.
-D 2026-06-24T15:16:53.666
+C When\srolling\sback\sa\sjournal\sthat\scontains\sa\ssuper-journal\spointer,\sonly\sattempt\sto\sunlink\sthe\ssuper-journal\sif\s(a)\sthe\sfilename\slooks\slike\sone\sthat\sSQLite\smight\shave\sgenerated,\sand\s(b)\sthe\ssuper-journal\scontains\sthe\sname\sof\sthe\sjournal\sbeing\srolled\sback.\sThis\sis\sto\slimit\sthe\sextent\sto\swhich\sSQLite\scan\sbe\scaused\sto\sdelete\sarbitrary\sfiles\sby\ssupplying\sit\swith\sa\scrafted\shot-journal.
+D 2026-06-24T20:28:31.446
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F src/os_unix.c 92a1773139e0c6206a927c7e1648b06287583570b6e253210a06ee3886739d39
F src/os_win.c 1ad2a8679104249197a9ea10d287136484fdf70c1a245bdda393bbfca2033980
F src/os_win.h 5e168adf482484327195d10f9c3bce3520f598e04e07ffe62c9c5a8067c1037b
-F src/pager.c 4d929597af269c40e4de77151a58f6e9d1f182bb9923fd03b2f8a46c05094cc3
+F src/pager.c a9fe20f18adfe7c3371cb68b42c723e9ce2a068a7558949216d9363359b267cf
F src/pager.h 6137149346e6c8a3ddc1eeb40aee46381e9bc8b0fcc6dda8a1efde993c2275b8
F src/parse.y d5a3c5b0277a441c38b35071c05e2b61ff5fc918a63309c809f4b6706179c320
F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484
F test/misc7.test d595599972ec0b436985f0f02f243b68500ffc977b9b3194ec66c0866cfddcab
F test/misc8.test 08d2380bc435486b12161521f225043ac2be26f02471c2c1ea4cac0b1548edbd
F test/misuse.test 859f37014d9824ca66bd90c36372c08c80c51c9593a7cfa8a31d4f92cd4d5b7f
-F test/mjournal.test 3570ca4241b0d19ca1f47616f5293722f7c2fc9f2d359fc7933d67ef51dc2137
+F test/mjournal.test f054788b7ca261e3825e306a1c714060941b709fe90fde2f14336f355300d1f0
F test/mmap1.test 18de3fd7b70a777af6004ca2feecfcdd3d0be17fa04058e808baf530c94b1a1d
F test/mmap2.test dba452dc7db91e9df10f70bdd73dc4190c7b8ee7b5133b4684f04277ada0b9ac
F test/mmap3.test b3c297e78e6a8520aafcc1a8f140535594c9086e
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigfault.tes fc5918e64f3867156fefe7cfca9d8e1f495134a5229b2b511b0dc11c07f2eab4
F test/temptrigger.test a00f258ed8d21a0e8fd4f322f15e8cfb5cef2e43655670e07a753e3fb4769d61
-F test/tester.tcl 2d943f60200e0a36bcd3f1f0baf181a751cd3604ef6b6bd4c8dc39b4e8a53116
+F test/tester.tcl dfd8dd05b54bc493a8db6cc1263ee364c36e3c27fab258dad7305cd79b948f3f
F test/testloadext.c 862b848783eaed9985fbce46c65cd214664376b549fae252b364d5d1ef350a27
F test/testrunner.tcl 8d92cacf9989aefdf33229c414adac56d389b5b6d9d31d9ebed34d5ab4e13833 x
F test/testrunner_data.tcl 48c8a230fcada37f4809f95c2ba49e44bc3d520b6165c09173249c6e65b01cc1
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P d9177ed965d1284c1c8d2c6fbb8db0dd89a42909ff0a3c23e2e761a6be419011
-Q +140a693ee0c0cfa8f332cd1bbdfe045c016e4bf5169d6dd94648cbe38d7517c6
-R 1f61b3093fa865b801ac97c7d1dc6aef
+P a9cea5b555eed4315e426ee8168029840cacb165d1fcd5e2d21a105db84dc46e
+Q +6d8da8861c8ccbfdbe838587dc2f5ef1817bc0af4a6167940446ee7bdc53955c
+R c7602b608ea6d4346a62553a77ccd9f6
U drh
-Z d755684f38209830a892294bb07f0996
+Z ade0c9258c13d6f538a26e7167f8af33
# Remove this line to create a well-formed Fossil manifest.
-a9cea5b555eed4315e426ee8168029840cacb165d1fcd5e2d21a105db84dc46e
+9471fa2c9ce03713b875b3350d65b5ed6e38e501585126bb3b90694800edfb25
return rc;
}
+/*
+** Check if zSuper is a valid super-journal name. There are two valid
+** formats:
+**
+** + The 3rd and 4th last bytes of the filename are ".9", and the
+** following 2 bytes are hex digits. This is a file created in 8.3
+** filenames mode.
+**
+** + The 3rd last byte of the filename is "9" and the filename
+** contains the string "-mj" starting at the 12th last byte.
+** All bytes following the "-mj" are hex digits.
+**
+** If the filename matches either of these patterns, return non-zero.
+** Otherwise, return zero.
+*/
+static int pagerIsSuperJrnlName(const char *zSuper){
+ const int nSuper = sqlite3Strlen30(zSuper);
+ int ii;
+
+ if( nSuper<4 ) return 0;
+ if( zSuper[nSuper-3]!='9' ) return 0;
+#ifdef SQLITE_ENABLE_8_3_NAMES
+ if( sqlite3Isxdigit(zSuper[nSuper-2])==0 ) return 0;
+ if( sqlite3Isxdigit(zSuper[nSuper-1])==0 ) return 0;
+ if( zSuper[nSuper-4]=='.' ) return 1;
+#endif
+ if( nSuper<12 ) return 0;
+ if( memcmp(&zSuper[nSuper-12], "-mj", 3) ) return 0;
+ for(ii=nSuper-9; ii<nSuper; ii++){
+ if( sqlite3Isxdigit(zSuper[ii])==0 ) return 0;
+ }
+ return 1;
+}
+
/*
** Parameter zSuper is the name of a super-journal file. A single journal
** file that referred to the super-journal file has just been rolled back.
i64 nSuperJournal; /* Size of super-journal file */
char *zJournal; /* Pointer to one journal within MJ file */
char *zFree = 0; /* Free this buffer */
+ int bSeen = 0; /* If super-journal contains pPager->zJournal */
+
+ /* Check if this looks like a real super-journal name. If it does not,
+ ** return SQLITE_OK without attempting to delete it. This is to limit
+ ** the degree to which a crafted journal file can be used to cause
+ ** SQLite to delete arbitrary files. */
+ if( pagerIsSuperJrnlName(zSuper)==0 ){
+ return SQLITE_OK;
+ }
/* Allocate space for both the pJournal and pSuper file descriptors.
** If successful, open the super-journal file for reading.
zJournal = zSuperJournal;
while( (zJournal-zSuperJournal)<nSuperJournal ){
- int exists;
- rc = sqlite3OsAccess(pVfs, zJournal, SQLITE_ACCESS_EXISTS, &exists);
- if( rc!=SQLITE_OK ){
- goto delsuper_out;
- }
- if( exists ){
- char *zSuperPtr = 0;
-
- /* One of the journals pointed to by the super-journal exists.
- ** Open it and check if it points at the super-journal. If
- ** so, return without deleting the super-journal file.
- ** NB: zJournal is really a MAIN_JOURNAL. But call it a
- ** SUPER_JOURNAL here so that the VFS will not send the zJournal
- ** name into sqlite3_database_file_object().
- */
- int c;
- int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_SUPER_JOURNAL);
- rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0);
+ if( strcmp(zJournal, pPager->zJournal)==0 ){
+ bSeen = 1;
+ }else{
+ int exists;
+ rc = sqlite3OsAccess(pVfs, zJournal, SQLITE_ACCESS_EXISTS, &exists);
if( rc!=SQLITE_OK ){
goto delsuper_out;
}
+ if( exists ){
+ char *zSuperPtr = 0;
+
+ /* One of the journals pointed to by the super-journal exists.
+ ** Open it and check if it points at the super-journal. If
+ ** so, return without deleting the super-journal file.
+ ** NB: zJournal is really a MAIN_JOURNAL. But call it a
+ ** SUPER_JOURNAL here so that the VFS will not send the zJournal
+ ** name into sqlite3_database_file_object().
+ */
+ int c;
+ int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_SUPER_JOURNAL);
+ rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0);
+ if( rc!=SQLITE_OK ){
+ goto delsuper_out;
+ }
- rc = readSuperJournal(pJournal, 1+(u64)pVfs->mxPathname, &zSuperPtr);
- sqlite3OsClose(pJournal);
- if( rc!=SQLITE_OK ){
- assert( zSuperPtr==0 );
- goto delsuper_out;
- }
+ rc = readSuperJournal(pJournal, 1+(u64)pVfs->mxPathname, &zSuperPtr);
+ sqlite3OsClose(pJournal);
+ if( rc!=SQLITE_OK ){
+ assert( zSuperPtr==0 );
+ goto delsuper_out;
+ }
- c = zSuperPtr!=0 && strcmp(zSuperPtr, zSuper)==0;
- freeSuperJournal(zSuperPtr);
- if( c ){
- /* We have a match. Do not delete the super-journal file. */
- goto delsuper_out;
+ c = zSuperPtr!=0 && strcmp(zSuperPtr, zSuper)==0;
+ freeSuperJournal(zSuperPtr);
+ if( c ){
+ /* We have a match. Do not delete the super-journal file. */
+ goto delsuper_out;
+ }
}
}
zJournal += (sqlite3Strlen30(zJournal)+1);
}
sqlite3OsClose(pSuper);
- rc = sqlite3OsDelete(pVfs, zSuper, 0);
+ if( bSeen ){
+ /* Only delete the super-journal if bSeen is true - indicating that
+ ** the super-journal contained a pointer to this database's journal
+ ** file. */
+ rc = sqlite3OsDelete(pVfs, zSuper, 0);
+ }
delsuper_out:
sqlite3_free(zFree);
SELECT type, name, tbl_name, sql FROM sqlite_schema
} {table t1 t1 {CREATE TABLE t1(x, y)}}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 4.0 {
+ CREATE TABLE t1(x INTEGER PRIMARY KEY);
+}
+db_save_and_close
+proc append_super_journal {jrnl mjname} {
+ binary scan $mjname c* bytes
+ set cksum 0
+ foreach b $bytes { incr cksum $b }
+ set fd [open $jrnl a+]
+ fconfigure $fd -translation binary -encoding iso8859-1
+ puts -nonewline $fd $mjname
+ puts -nonewline $fd [binary format I [string length $mjname]]
+ puts -nonewline $fd [binary format I $cksum]
+ puts -nonewline $fd [binary decode hex "d9d505f920a163d7"]
+ close $fd
+}
+
+set c 0
+ifcapable 8_3_names { set c 1 }
+
+set tests [subst {
+ 1 notamasterjournal 0
+ 2 master.9FF $c
+ 3 master-mj1234569AA 1
+ 4 master-mj123456_AA 0
+ 5 abc 0
+ 6 masterr9FF 0
+ 7 master-fj123456_AA 0
+ 8 -mj1234569AA 1
+ 9 1-mj1234569AA 1
+ 10 .9AB 0
+ 11 master.9X2 0
+ 12 master.92X 0
+ 13 master-mj12G4569AA 0
+}]
+
+foreach {tn mjname bDel} $tests {
+ for {set content 0} {$content<2} {incr content} {
+ db_restore_and_reopen
+ execsql {
+ PRAGMA synchronous = OFF;
+ BEGIN;
+ INSERT INTO t1 DEFAULT VALUES;
+ }
+
+ db_save_and_close
+ db_restore
+
+ append_super_journal test.db-journal $mjname
+ set fd [open $mjname w]
+ if {$content} {
+ set jname [file normalize test.db-journal]
+ if {$::tcl_platform(platform) eq "windows"} {
+ set jname [string map [list / \\] $jname]
+ }
+ puts -nonewline $fd $jname
+ puts -nonewline $fd "\0"
+ set mjexists [expr !$bDel]
+ } else {
+ puts -nonewline $fd "helloworld"
+ set mjexists 1
+ }
+ close $fd
+
+ sqlite3 db test.db
+ do_test 4.$tn.$content.1 {
+ execsql { SELECT * FROM t1 }
+ file exists $mjname
+ } $mjexists
+ do_test 4.$tn.$content.2 {
+ file exists test.db-journal
+ } {0}
+
+ forcedelete $mjname
+ }
+}
finish_test
+
+
for {set i 0} {$i<$nRetry} {incr i} {
set rc [catch {
if {$force} {
- file delete -force $filename
+ file delete -force -- $filename
} else {
- file delete $filename
+ file delete -- $filename
}
} msg]
if {$rc==0} break
if {$rc} { error $msg }
} else {
if {$force} {
- file delete -force $filename
+ file delete -force -- $filename
} else {
- file delete $filename
+ file delete -- $filename
}
}
}