1# 2007 May 10 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. The focus 12# of this file is checking the libraries response to subtly corrupting 13# the database file by changing the values of pseudo-randomly selected 14# bytes. 15# 16# $Id: fuzz3.test,v 1.3 2009/01/05 17:19:03 drh Exp $ 17 18set testdir [file dirname $argv0] 19source $testdir/tester.tcl 20 21# These tests deal with corrupt database files 22# 23database_may_be_corrupt 24 25expr srand(123) 26 27proc rstring {n} { 28 set str s 29 while {[string length $str] < $n} { 30 append str [expr rand()] 31 } 32 return [string range $str 0 $n] 33} 34 35# Return a randomly generated SQL literal. 36# 37proc rvalue {} { 38 switch -- [expr int(rand()*5)] { 39 0 { # SQL NULL value. 40 return NULL 41 } 42 1 { # Integer value. 43 return [expr int(rand()*1024)] 44 } 45 2 { # Real value. 46 return [expr rand()] 47 } 48 3 { # String value. 49 set n [expr int(rand()*2500)] 50 return "'[rstring $n]'" 51 } 52 4 { # Blob value. 53 set n [expr int(rand()*2500)] 54 return "CAST('[rstring $n]' AS BLOB)" 55 } 56 } 57} 58 59proc db_checksum {} { 60 set cksum [execsql { SELECT md5sum(a, b, c) FROM t1 }] 61 append cksum [execsql { SELECT md5sum(d, e, f) FROM t2 }] 62 set cksum 63} 64 65# Modify a single byte in the file 'test.db' using tcl IO commands. The 66# argument value, which must be an integer, determines both the offset of 67# the byte that is modified, and the value that it is set to. The lower 68# 8 bits of iMod determine the new byte value. The offset of the byte 69# modified is the value of ($iMod >> 8). 70# 71# The return value is the iMod value required to restore the file 72# to its original state. The command: 73# 74# modify_database [modify_database $x] 75# 76# leaves the file in the same state as it was in at the start of the 77# command (assuming that the file is at least ($x>>8) bytes in size). 78# 79proc modify_database {iMod} { 80 set blob [binary format c [expr {$iMod&0xFF}]] 81 set offset [expr {$iMod>>8}] 82 83 set fd [open test.db r+] 84 fconfigure $fd -encoding binary -translation binary 85 seek $fd $offset 86 set old_blob [read $fd 1] 87 seek $fd $offset 88 puts -nonewline $fd $blob 89 close $fd 90 91 binary scan $old_blob c iOld 92 return [expr {($offset<<8) + ($iOld&0xFF)}] 93} 94 95proc purge_pcache {} { 96 ifcapable !memorymanage { 97 db close 98 sqlite3 db test.db 99 } else { 100 sqlite3_release_memory 10000000 101 } 102 if {[lindex [pcache_stats] 1] != 0} { 103 error "purge_pcache failed: [pcache_stats]" 104 } 105} 106 107# This block creates a database to work with. 108# 109do_test fuzz3-1 { 110 execsql { 111 BEGIN; 112 CREATE TABLE t1(a, b, c); 113 CREATE TABLE t2(d, e, f); 114 CREATE INDEX i1 ON t1(a, b, c); 115 CREATE INDEX i2 ON t2(d, e, f); 116 } 117 for {set i 0} {$i < 50} {incr i} { 118 execsql "INSERT INTO t1 VALUES([rvalue], [rvalue], [rvalue])" 119 execsql "INSERT INTO t2 VALUES([rvalue], [rvalue], [rvalue])" 120 } 121 execsql COMMIT 122} {} 123 124set ::cksum [db_checksum] 125do_test fuzz3-2 { 126 db_checksum 127} $::cksum 128 129for {set ii 0} {$ii < 5000} {incr ii} { 130 purge_pcache 131 132 # Randomly modify a single byte of the database file somewhere within 133 # the first 100KB of the file. 134 set iNew [expr int(rand()*5*1024*256)] 135 set iOld [modify_database $iNew] 136 137 set iTest 0 138 foreach sql { 139 {SELECT * FROM t2 ORDER BY d} 140 {SELECT * FROM t1} 141 {SELECT * FROM t2} 142 {SELECT * FROM t1 ORDER BY a} 143 {SELECT * FROM t1 WHERE a = (SELECT a FROM t1 WHERE rowid=25)} 144 {SELECT * FROM t2 WHERE d = (SELECT d FROM t2 WHERE rowid=1)} 145 {SELECT * FROM t2 WHERE d = (SELECT d FROM t2 WHERE rowid=50)} 146 {PRAGMA integrity_check} 147 } { 148 do_test fuzz3-$ii.$iNew.[incr iTest] { 149 foreach {rc msg} [catchsql $sql] {} 150 if {$rc == 0 151 || $msg eq "database or disk is full" 152 || $msg eq "database disk image is malformed" 153 || $msg eq "file is encrypted or is not a database" 154 || [string match "malformed database schema*" $msg] 155 } { 156 set msg ok 157 } 158 set msg 159 } {ok} 160 } 161 162 # Restore the original database file content. Test that the correct 163 # checksum is now returned. 164 # 165 purge_pcache 166 modify_database $iOld 167 do_test fuzz3-$ii.$iNew.[incr iTest] { 168 db_checksum 169 } $::cksum 170} 171 172finish_test 173