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