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