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