corrupt.test 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. # 2004 August 30 {}
  2. #
  3. # The author disclaims copyright to this source code. In place of
  4. # a legal notice, here is a blessing:
  5. #
  6. # May you do good and not evil.
  7. # May you find forgiveness for yourself and forgive others.
  8. # May you share freely, never taking more than you give.
  9. #
  10. #***********************************************************************
  11. # This file implements regression tests for SQLite library.
  12. #
  13. # This file implements tests to make sure SQLite does not crash or
  14. # segfault if it sees a corrupt database file.
  15. #
  16. # $Id: corrupt.test,v 1.12 2009/07/13 09:41:45 danielk1977 Exp $
  17. catch {forcedelete test.db test.db-journal test.bu}
  18. set testdir [file dirname $argv0]
  19. source $testdir/tester.tcl
  20. # Do not use a codec for tests in this file, as the database file is
  21. # manipulated directly using tcl scripts (using the [hexio_write] command).
  22. #
  23. do_not_use_codec
  24. # Construct a large database for testing.
  25. #
  26. do_test corrupt-1.1 {
  27. execsql {
  28. BEGIN;
  29. CREATE TABLE t1(x);
  30. INSERT INTO t1 VALUES(randstr(100,100));
  31. INSERT INTO t1 VALUES(randstr(90,90));
  32. INSERT INTO t1 VALUES(randstr(80,80));
  33. INSERT INTO t1 SELECT x || randstr(5,5) FROM t1;
  34. INSERT INTO t1 SELECT x || randstr(6,6) FROM t1;
  35. INSERT INTO t1 SELECT x || randstr(7,7) FROM t1;
  36. INSERT INTO t1 SELECT x || randstr(8,8) FROM t1;
  37. INSERT INTO t1 VALUES(randstr(3000,3000));
  38. INSERT INTO t1 SELECT x || randstr(9,9) FROM t1;
  39. INSERT INTO t1 SELECT x || randstr(10,10) FROM t1;
  40. INSERT INTO t1 SELECT x || randstr(11,11) FROM t1;
  41. INSERT INTO t1 SELECT x || randstr(12,12) FROM t1;
  42. CREATE INDEX t1i1 ON t1(x);
  43. CREATE TABLE t2 AS SELECT * FROM t1;
  44. DELETE FROM t2 WHERE rowid%5!=0;
  45. COMMIT;
  46. }
  47. } {}
  48. integrity_check corrupt-1.2
  49. # Setup for the tests. Make a backup copy of the good database in test.bu.
  50. # Create a string of garbage data that is 256 bytes long.
  51. #
  52. forcecopy test.db test.bu
  53. set fsize [file size test.db]
  54. set junk "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  55. while {[string length $junk]<256} {append junk $junk}
  56. set junk [string range $junk 0 255]
  57. # Go through the database and write garbage data into each 256 segment
  58. # of the file. Then do various operations on the file to make sure that
  59. # the database engine can recover gracefully from the corruption.
  60. #
  61. for {set i [expr {1*256}]} {$i<$fsize-256} {incr i 256} {
  62. set tn [expr {$i/256}]
  63. db close
  64. forcecopy test.bu test.db
  65. set fd [open test.db r+]
  66. fconfigure $fd -translation binary
  67. seek $fd $i
  68. puts -nonewline $fd $junk
  69. close $fd
  70. do_test corrupt-2.$tn.1 {
  71. sqlite3 db test.db
  72. catchsql {SELECT count(*) FROM sqlite_master}
  73. set x {}
  74. } {}
  75. do_test corrupt-2.$tn.2 {
  76. catchsql {SELECT count(*) FROM t1}
  77. set x {}
  78. } {}
  79. do_test corrupt-2.$tn.3 {
  80. catchsql {SELECT count(*) FROM t1 WHERE x>'abcdef'}
  81. set x {}
  82. } {}
  83. do_test corrupt-2.$tn.4 {
  84. catchsql {SELECT count(*) FROM t2}
  85. set x {}
  86. } {}
  87. do_test corrupt-2.$tn.5 {
  88. catchsql {CREATE TABLE t3 AS SELECT * FROM t1}
  89. set x {}
  90. } {}
  91. do_test corrupt-2.$tn.6 {
  92. catchsql {DROP TABLE t1}
  93. set x {}
  94. } {}
  95. do_test corrupt-2.$tn.7 {
  96. catchsql {PRAGMA integrity_check}
  97. set x {}
  98. } {}
  99. # Check that no page references were leaked.
  100. do_test corrupt-2.$tn.8 {
  101. set bt [btree_from_db db]
  102. db_enter db
  103. array set stats [btree_pager_stats $bt]
  104. db_leave db
  105. set stats(ref)
  106. } {0}
  107. }
  108. #------------------------------------------------------------------------
  109. # For these tests, swap the rootpage entries of t1 (a table) and t1i1 (an
  110. # index on t1) in sqlite_master. Then perform a few different queries
  111. # and make sure this is detected as corruption.
  112. #
  113. do_test corrupt-3.1 {
  114. db close
  115. forcecopy test.bu test.db
  116. sqlite3 db test.db
  117. list
  118. } {}
  119. do_test corrupt-3.2 {
  120. set t1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1i1'}]
  121. set t1i1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1'}]
  122. set cookie [expr [execsql {PRAGMA schema_version}] + 1]
  123. execsql "
  124. PRAGMA writable_schema = 1;
  125. UPDATE sqlite_master SET rootpage = $t1_r WHERE name = 't1';
  126. UPDATE sqlite_master SET rootpage = $t1i1_r WHERE name = 't1i1';
  127. PRAGMA writable_schema = 0;
  128. PRAGMA schema_version = $cookie;
  129. "
  130. } {}
  131. # This one tests the case caught by code in checkin [2313].
  132. do_test corrupt-3.3 {
  133. db close
  134. sqlite3 db test.db
  135. catchsql {
  136. INSERT INTO t1 VALUES('abc');
  137. }
  138. } {1 {database disk image is malformed}}
  139. do_test corrupt-3.4 {
  140. db close
  141. sqlite3 db test.db
  142. catchsql {
  143. SELECT * FROM t1;
  144. }
  145. } {1 {database disk image is malformed}}
  146. do_test corrupt-3.5 {
  147. db close
  148. sqlite3 db test.db
  149. catchsql {
  150. SELECT * FROM t1 WHERE oid = 10;
  151. }
  152. } {1 {database disk image is malformed}}
  153. do_test corrupt-3.6 {
  154. db close
  155. sqlite3 db test.db
  156. catchsql {
  157. SELECT * FROM t1 WHERE x = 'abcde';
  158. }
  159. } {1 {database disk image is malformed}}
  160. do_test corrupt-4.1 {
  161. db close
  162. forcedelete test.db test.db-journal
  163. sqlite3 db test.db
  164. execsql {
  165. PRAGMA page_size = 1024;
  166. CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
  167. }
  168. for {set i 0} {$i < 10} {incr i} {
  169. set text [string repeat $i 220]
  170. execsql { INSERT INTO t1 VALUES($i, $text) }
  171. }
  172. execsql { CREATE INDEX i1 ON t1(b) }
  173. } {}
  174. do_test corrupt-4.2 {
  175. set iRoot [db one {SELECT rootpage FROM sqlite_master WHERE name = 'i1'}]
  176. set iOffset [hexio_get_int [hexio_read test.db [expr 12+($iRoot-1)*1024] 2]]
  177. set data [hexio_render_int32 [expr $iRoot - 1]]
  178. hexio_write test.db [expr ($iRoot-1)*1024 + $iOffset] $data
  179. db close
  180. sqlite3 db test.db
  181. # The following DELETE statement attempts to delete a cell stored on the
  182. # root page of index i1. After this cell is deleted it must be replaced
  183. # by a cell retrieved from the child page (a leaf) of the deleted cell.
  184. # This will fail, as the block modified the database image so that the
  185. # child page of the deleted cell is from a table (intkey) b-tree, not an
  186. # index b-tree as expected. At one point this was causing an assert()
  187. # to fail.
  188. catchsql { DELETE FROM t1 WHERE rowid = 3 }
  189. } {1 {database disk image is malformed}}
  190. do_test corrupt-5.1 {
  191. db close
  192. forcedelete test.db test.db-journal
  193. sqlite3 db test.db
  194. execsql { PRAGMA page_size = 1024 }
  195. set ct "CREATE TABLE t1(c0 "
  196. set i 0
  197. while {[string length $ct] < 950} { append ct ", c[incr i]" }
  198. append ct ")"
  199. execsql $ct
  200. } {}
  201. do_test corrupt-5.2 {
  202. db close
  203. hexio_write test.db 108 00000000
  204. sqlite3 db test.db
  205. catchsql { SELECT * FROM sqlite_master }
  206. } {1 {database disk image is malformed}}
  207. # At one point, the specific corruption caused by this test case was
  208. # causing a buffer overwrite. Although a crash was never demonstrated,
  209. # running this testcase under valgrind revealed the problem.
  210. do_test corrupt-6.1 {
  211. db close
  212. forcedelete test.db test.db-journal
  213. sqlite3 db test.db
  214. execsql {
  215. PRAGMA page_size = 1024; CREATE TABLE t1(x);
  216. }
  217. # The root page of t1 is 1024 bytes in size. The header is 8 bytes, and
  218. # each of the cells inserted by the following INSERT statements consume
  219. # 16 bytes (including the 2 byte cell-offset array entry). So the page
  220. # can contain up to 63 cells.
  221. for {set i 0} {$i < 63} {incr i} {
  222. execsql { INSERT INTO t1 VALUES( randomblob(10) ) }
  223. }
  224. # Free the cell stored right at the end of the page (at offset pgsz-14).
  225. execsql { DELETE FROM t1 WHERE rowid=1 }
  226. set rootpage [db one {SELECT rootpage FROM sqlite_master WHERE name = 't1'}]
  227. db close
  228. set offset [expr ($rootpage * 1024)-14+2]
  229. hexio_write test.db $offset 00FF
  230. sqlite3 db test.db
  231. catchsql { INSERT INTO t1 VALUES( randomblob(10) ) }
  232. } {1 {database disk image is malformed}}
  233. ifcapable oversize_cell_check {
  234. db close
  235. forcedelete test.db test.db-journal
  236. sqlite3 db test.db
  237. execsql {
  238. PRAGMA page_size = 1024; CREATE TABLE t1(x);
  239. }
  240. do_test corrupt-7.1 {
  241. for {set i 0} {$i < 39} {incr i} {
  242. execsql {
  243. INSERT INTO t1 VALUES(X'000100020003000400050006000700080009000A');
  244. }
  245. }
  246. } {}
  247. db close
  248. # Corrupt the root page of table t1 so that the first offset in the
  249. # cell-offset array points to the data for the SQL blob associated with
  250. # record (rowid=10). The root page still passes the checks in btreeInitPage(),
  251. # because the start of said blob looks like the start of a legitimate
  252. # page cell.
  253. #
  254. # Test case cc-2 overwrites the blob so that it no longer looks like a
  255. # real cell. But, by the time it is overwritten, btreeInitPage() has already
  256. # initialized the root page, so no corruption is detected.
  257. #
  258. # Test case cc-3 inserts an extra record into t1, forcing balance-deeper
  259. # to run. After copying the contents of the root page to the new child,
  260. # btreeInitPage() is called on the child. This time, it detects corruption
  261. # (because the start of the blob associated with the (rowid=10) record
  262. # no longer looks like a real cell). At one point the code assumed that
  263. # detecting corruption was not possible at that point, and an assert() failed.
  264. #
  265. set fd [open test.db r+]
  266. fconfigure $fd -translation binary -encoding binary
  267. seek $fd [expr 1024+8]
  268. puts -nonewline $fd "\x03\x14"
  269. close $fd
  270. sqlite3 db test.db
  271. do_test corrupt-7.2 {
  272. execsql {
  273. UPDATE t1 SET x = X'870400020003000400050006000700080009000A'
  274. WHERE rowid = 10;
  275. }
  276. } {}
  277. do_test corrupt-7.3 {
  278. catchsql {
  279. INSERT INTO t1 VALUES(X'000100020003000400050006000700080009000A');
  280. }
  281. } {1 {database disk image is malformed}}
  282. }
  283. db close
  284. forcedelete test.db test.db-journal
  285. do_test corrupt-8.1 {
  286. sqlite3 db test.db
  287. execsql {
  288. PRAGMA page_size = 1024;
  289. PRAGMA secure_delete = on;
  290. PRAGMA auto_vacuum = 0;
  291. CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
  292. INSERT INTO t1 VALUES(5, randomblob(1900));
  293. }
  294. hexio_write test.db 2044 [hexio_render_int32 2]
  295. hexio_write test.db 24 [hexio_render_int32 45]
  296. catchsql { INSERT OR REPLACE INTO t1 VALUES(5, randomblob(1900)) }
  297. } {1 {database disk image is malformed}}
  298. db close
  299. forcedelete test.db test.db-journal
  300. do_test corrupt-8.2 {
  301. sqlite3 db test.db
  302. execsql {
  303. PRAGMA page_size = 1024;
  304. PRAGMA secure_delete = on;
  305. PRAGMA auto_vacuum = 0;
  306. CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
  307. INSERT INTO t1 VALUES(5, randomblob(900));
  308. INSERT INTO t1 VALUES(6, randomblob(900));
  309. }
  310. hexio_write test.db 2047 FF
  311. hexio_write test.db 24 [hexio_render_int32 45]
  312. catchsql { INSERT INTO t1 VALUES(4, randomblob(1900)) }
  313. } {1 {database disk image is malformed}}
  314. finish_test