2018-11-05 20:37:33 +00:00
/*
* * 2018 - 11 - 01
* *
* * The author disclaims copyright to this source code . In place of
* * a legal notice , here is a blessing :
* *
* * May you do good and not evil .
* * May you find forgiveness for yourself and forgive others .
* * May you share freely , never taking more than you give .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * This file contains code to implement the " changesetfuzz " command
* * line utility for fuzzing changeset blobs without corrupting them .
*/
/************************************************************************
* * USAGE :
* *
* * This program may be invoked in two ways :
* *
* * changesetfuzz INPUT
* * changesetfuzz INPUT SEED N
* *
* * Argument INPUT must be the name of a file containing a binary changeset .
* * In the first form above , this program outputs a human - readable version
* * of the same changeset . This is chiefly for debugging .
* *
2018-11-07 20:07:28 +00:00
* * As well as changesets , this program can also dump and fuzz patchsets .
* * The term " changeset " is used for both patchsets and changesets from this
* * point on .
* *
2018-11-05 20:37:33 +00:00
* * In the second form , arguments SEED and N must both be integers . In this
* * case , this program writes N binary changesets to disk . Each output
* * changeset is a slightly modified - " fuzzed " - version of the input .
* * The output changesets are written to files name " INPUT-$n " , where $ n is
* * an integer between 0 and N - 1 , inclusive . Output changesets are always
* * well - formed . Parameter SEED is used to seed the PRNG - any two
* * invocations of this program with the same SEED and input changeset create
* * the same N output changesets .
* *
* * The ways in which an input changeset may be fuzzed are as follows :
* *
* * 1. Any two values within the changeset may be exchanged .
* *
* * 2. Any TEXT , BLOB , INTEGER or REAL value within the changeset
* * may have a single bit of its content flipped .
* *
* * 3. Any value within a changeset may be replaced by a pseudo - randomly
* * generated value .
* *
* * The above operations never set a PRIMARY KEY column to NULL . Nor do they
* * set any value to " undefined " , or replace any " undefined " value with
* * another . Any such operation risks producing a changeset that is not
* * well - formed .
* *
* * 4. A single change may be duplicated .
* *
* * 5. A single change may be removed , so long as this does not mean that
* * there are zero changes following a table - header within the changeset .
* *
* * 6. A single change may have its type ( INSERT , DELETE , UPDATE ) changed .
* * If an INSERT is changed to a DELETE ( or vice versa ) , the type is
* * simply changed - no other modifications are required . If an INSERT
* * or DELETE is changed to an UPDATE , then the single record is duplicated
* * ( as both the old . * and new . * records of the new UPDATE change ) . If an
* * UPDATE is changed to a DELETE or INSERT , the new . * record is discarded
* * and any " undefined " fields replaced with pseudo - randomly generated
2018-11-06 20:08:03 +00:00
* * values .
2018-11-05 20:37:33 +00:00
* *
* * 7. An UPDATE change that modifies N table columns may be modified so
* * that it updates N - 1 columns , so long as ( N > 1 ) .
* *
2018-11-06 20:08:03 +00:00
* * 8. The " indirect " flag may be toggled for any change .
* *
* * Entire group of changes may also be operated on :
* *
* * 9. Duplicate an existing group .
* *
* * 10. Remove an existing group .
* *
* * 11. The positions of two groups may be exchanged .
* *
* * There are also schema changes :
* *
* * 12. A non - PK column may be added to a table . In this case a NULL
* * value is appended to all records .
* *
* * 13. A PK column may be added to a table . In this case a non - NULL
* * value is appended to all INSERT , DELETE and UPDATE old . * records .
* * An " undefined " is appended to new . * UPDATE records .
* *
2018-11-07 17:52:29 +00:00
* * 14. A column may be removed from a table , provided that it is not the
* * only PRIMARY KEY column in the table . In this case the corresponding
2018-11-06 20:08:03 +00:00
* * field is removed from all records . In cases where this leaves an UPDATE
2018-11-07 17:52:29 +00:00
* * with no non - PK , non - undefined fields , the entire change is removed .
2018-11-05 20:37:33 +00:00
*/
# include "sqlite3.h"
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <assert.h>
# include <ctype.h>
2018-11-06 20:08:03 +00:00
# define FUZZ_VALUE_SUB 1 /* Replace one value with a copy of another */
# define FUZZ_VALUE_MOD 2 /* Modify content by 1 bit */
# define FUZZ_VALUE_RND 3 /* Replace with pseudo-random value */
# define FUZZ_CHANGE_DUP 4 /* Duplicate an existing change */
# define FUZZ_CHANGE_DEL 5 /* Completely remove one change */
# define FUZZ_CHANGE_TYPE 6 /* Change the type of one change */
# define FUZZ_CHANGE_FIELD 7 /* Change an UPDATE to modify fewer columns */
# define FUZZ_CHANGE_INDIRECT 8 /* Toggle the "indirect" flag of a change */
# define FUZZ_GROUP_DUP 9 /* Duplicate a change group */
# define FUZZ_GROUP_DEL 10 /* Delete an entire change group */
# define FUZZ_GROUP_SWAP 11 /* Exchange the position of two groups */
2018-11-05 20:37:33 +00:00
2018-11-06 20:08:03 +00:00
# define FUZZ_COLUMN_ADD 12 /* Add column to table definition */
# define FUZZ_COLUMN_ADDPK 13 /* Add PK column to table definition */
# define FUZZ_COLUMN_DEL 14 /* Remove column from table definition */
2018-11-05 20:37:33 +00:00
typedef unsigned char u8 ;
typedef sqlite3_uint64 u64 ;
typedef sqlite3_int64 i64 ;
typedef unsigned int u32 ;
/*
* * Show a usage message on stderr then quit .
*/
static void usage ( const char * argv0 ) {
fprintf ( stderr , " Usage: %s FILENAME ?SEED N? \n " , argv0 ) ;
exit ( 1 ) ;
}
/*
* * Read the content of a disk file into an in - memory buffer
*/
static void fuzzReadFile ( const char * zFilename , int * pSz , void * * ppBuf ) {
FILE * f ;
2019-01-08 20:02:48 +00:00
sqlite3_int64 sz ;
2018-11-05 20:37:33 +00:00
void * pBuf ;
f = fopen ( zFilename , " rb " ) ;
if ( f = = 0 ) {
fprintf ( stderr , " cannot open \" %s \" for reading \n " , zFilename ) ;
exit ( 1 ) ;
}
fseek ( f , 0 , SEEK_END ) ;
2019-01-08 20:02:48 +00:00
sz = ftell ( f ) ;
2018-11-05 20:37:33 +00:00
rewind ( f ) ;
2019-01-08 20:02:48 +00:00
pBuf = sqlite3_malloc64 ( sz ? sz : 1 ) ;
2018-11-05 20:37:33 +00:00
if ( pBuf = = 0 ) {
fprintf ( stderr , " cannot allocate %d to hold content of \" %s \" \n " ,
2019-04-01 03:07:21 +00:00
( int ) sz , zFilename ) ;
2018-11-05 20:37:33 +00:00
exit ( 1 ) ;
}
if ( sz > 0 ) {
2019-04-01 03:07:21 +00:00
if ( fread ( pBuf , ( size_t ) sz , 1 , f ) ! = 1 ) {
fprintf ( stderr , " cannot read all %d bytes of \" %s \" \n " ,
( int ) sz , zFilename ) ;
2018-11-05 20:37:33 +00:00
exit ( 1 ) ;
}
fclose ( f ) ;
}
2019-04-01 03:07:21 +00:00
* pSz = ( int ) sz ;
2018-11-05 20:37:33 +00:00
* ppBuf = pBuf ;
}
2018-11-07 17:52:29 +00:00
/*
* * Write the contents of buffer pBuf , size nBuf bytes , into file zFilename
* * on disk . zFilename , if it already exists , is clobbered .
*/
2018-11-05 20:37:33 +00:00
static void fuzzWriteFile ( const char * zFilename , void * pBuf , int nBuf ) {
FILE * f ;
f = fopen ( zFilename , " wb " ) ;
if ( f = = 0 ) {
fprintf ( stderr , " cannot open \" %s \" for writing \n " , zFilename ) ;
exit ( 1 ) ;
}
if ( fwrite ( pBuf , nBuf , 1 , f ) ! = 1 ) {
fprintf ( stderr , " cannot write to \" %s \" \n " , zFilename ) ;
exit ( 1 ) ;
}
fclose ( f ) ;
}
static int fuzzCorrupt ( ) {
return SQLITE_CORRUPT ;
}
/*************************************************************************
* * The following block is a copy of the implementation of SQLite function
* * sqlite3_randomness . This version has two important differences :
* *
* * 1. It always uses the same seed . So the sequence of random data output
* * is the same for every run of the program .
* *
* * 2. It is not threadsafe .
*/
static struct sqlite3PrngType {
unsigned char i , j ; /* State variables */
unsigned char s [ 256 ] ; /* State variables */
} sqlite3Prng = {
0xAF , 0x28 ,
{
0x71 , 0xF5 , 0xB4 , 0x6E , 0x80 , 0xAB , 0x1D , 0xB8 ,
0xFB , 0xB7 , 0x49 , 0xBF , 0xFF , 0x72 , 0x2D , 0x14 ,
0x79 , 0x09 , 0xE3 , 0x78 , 0x76 , 0xB0 , 0x2C , 0x0A ,
0x8E , 0x23 , 0xEE , 0xDF , 0xE0 , 0x9A , 0x2F , 0x67 ,
0xE1 , 0xBE , 0x0E , 0xA7 , 0x08 , 0x97 , 0xEB , 0x77 ,
0x78 , 0xBA , 0x9D , 0xCA , 0x49 , 0x4C , 0x60 , 0x9A ,
0xF6 , 0xBD , 0xDA , 0x7F , 0xBC , 0x48 , 0x58 , 0x52 ,
0xE5 , 0xCD , 0x83 , 0x72 , 0x23 , 0x52 , 0xFF , 0x6D ,
0xEF , 0x0F , 0x82 , 0x29 , 0xA0 , 0x83 , 0x3F , 0x7D ,
0xA4 , 0x88 , 0x31 , 0xE7 , 0x88 , 0x92 , 0x3B , 0x9B ,
0x3B , 0x2C , 0xC2 , 0x4C , 0x71 , 0xA2 , 0xB0 , 0xEA ,
0x36 , 0xD0 , 0x00 , 0xF1 , 0xD3 , 0x39 , 0x17 , 0x5D ,
0x2A , 0x7A , 0xE4 , 0xAD , 0xE1 , 0x64 , 0xCE , 0x0F ,
0x9C , 0xD9 , 0xF5 , 0xED , 0xB0 , 0x22 , 0x5E , 0x62 ,
0x97 , 0x02 , 0xA3 , 0x8C , 0x67 , 0x80 , 0xFC , 0x88 ,
0x14 , 0x0B , 0x15 , 0x10 , 0x0F , 0xC7 , 0x40 , 0xD4 ,
0xF1 , 0xF9 , 0x0E , 0x1A , 0xCE , 0xB9 , 0x1E , 0xA1 ,
0x72 , 0x8E , 0xD7 , 0x78 , 0x39 , 0xCD , 0xF4 , 0x5D ,
0x2A , 0x59 , 0x26 , 0x34 , 0xF2 , 0x73 , 0x0B , 0xA0 ,
0x02 , 0x51 , 0x2C , 0x03 , 0xA3 , 0xA7 , 0x43 , 0x13 ,
0xE8 , 0x98 , 0x2B , 0xD2 , 0x53 , 0xF8 , 0xEE , 0x91 ,
0x7D , 0xE7 , 0xE3 , 0xDA , 0xD5 , 0xBB , 0xC0 , 0x92 ,
0x9D , 0x98 , 0x01 , 0x2C , 0xF9 , 0xB9 , 0xA0 , 0xEB ,
0xCF , 0x32 , 0xFA , 0x01 , 0x49 , 0xA5 , 0x1D , 0x9A ,
0x76 , 0x86 , 0x3F , 0x40 , 0xD4 , 0x89 , 0x8F , 0x9C ,
0xE2 , 0xE3 , 0x11 , 0x31 , 0x37 , 0xB2 , 0x49 , 0x28 ,
0x35 , 0xC0 , 0x99 , 0xB6 , 0xD0 , 0xBC , 0x66 , 0x35 ,
0xF7 , 0x83 , 0x5B , 0xD7 , 0x37 , 0x1A , 0x2B , 0x18 ,
0xA6 , 0xFF , 0x8D , 0x7C , 0x81 , 0xA8 , 0xFC , 0x9E ,
0xC4 , 0xEC , 0x80 , 0xD0 , 0x98 , 0xA7 , 0x76 , 0xCC ,
0x9C , 0x2F , 0x7B , 0xFF , 0x8E , 0x0E , 0xBB , 0x90 ,
0xAE , 0x13 , 0x06 , 0xF5 , 0x1C , 0x4E , 0x52 , 0xF7
}
} ;
/*
* * Generate and return single random byte
*/
static unsigned char fuzzRandomByte ( void ) {
unsigned char t ;
sqlite3Prng . i + + ;
t = sqlite3Prng . s [ sqlite3Prng . i ] ;
sqlite3Prng . j + = t ;
sqlite3Prng . s [ sqlite3Prng . i ] = sqlite3Prng . s [ sqlite3Prng . j ] ;
sqlite3Prng . s [ sqlite3Prng . j ] = t ;
t + = sqlite3Prng . s [ sqlite3Prng . i ] ;
return sqlite3Prng . s [ t ] ;
}
/*
* * Return N random bytes .
*/
static void fuzzRandomBlob ( int nBuf , unsigned char * zBuf ) {
int i ;
for ( i = 0 ; i < nBuf ; i + + ) {
zBuf [ i ] = fuzzRandomByte ( ) ;
}
}
/*
* * Return a random integer between 0 and nRange ( not inclusive ) .
*/
static unsigned int fuzzRandomInt ( unsigned int nRange ) {
unsigned int ret ;
assert ( nRange > 0 ) ;
fuzzRandomBlob ( sizeof ( ret ) , ( unsigned char * ) & ret ) ;
return ( ret % nRange ) ;
}
static u64 fuzzRandomU64 ( ) {
u64 ret ;
fuzzRandomBlob ( sizeof ( ret ) , ( unsigned char * ) & ret ) ;
return ret ;
}
static void fuzzRandomSeed ( unsigned int iSeed ) {
int i ;
for ( i = 0 ; i < 256 ; i + = 4 ) {
sqlite3Prng . s [ i ] ^ = ( ( iSeed > > 24 ) & 0xFF ) ;
sqlite3Prng . s [ i + 1 ] ^ = ( ( iSeed > > 16 ) & 0xFF ) ;
sqlite3Prng . s [ i + 2 ] ^ = ( ( iSeed > > 8 ) & 0xFF ) ;
sqlite3Prng . s [ i + 3 ] ^ = ( ( iSeed > > 0 ) & 0xFF ) ;
}
}
/*
* * End of code for generating pseudo - random values .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
typedef struct FuzzChangeset FuzzChangeset ;
typedef struct FuzzChangesetGroup FuzzChangesetGroup ;
typedef struct FuzzChange FuzzChange ;
/*
* * Object containing partially parsed changeset .
*/
struct FuzzChangeset {
2018-11-07 17:52:29 +00:00
int bPatchset ; /* True for a patchset */
2018-11-05 20:37:33 +00:00
FuzzChangesetGroup * * apGroup ; /* Array of groups in changeset */
int nGroup ; /* Number of items in list pGroup */
2018-11-06 20:08:03 +00:00
u8 * * apVal ; /* Array of all values in changeset */
int nVal ; /* Number of used slots in apVal[] */
2018-11-05 20:37:33 +00:00
int nChange ; /* Number of changes in changeset */
int nUpdate ; /* Number of UPDATE changes in changeset */
} ;
2018-11-07 17:52:29 +00:00
/*
* * There is one object of this type for each change - group ( table header )
* * in the input changeset .
*/
2018-11-05 20:37:33 +00:00
struct FuzzChangesetGroup {
const char * zTab ; /* Name of table */
int nCol ; /* Number of columns in table */
u8 * aPK ; /* PK array for this table */
u8 * aChange ; /* Buffer containing array of changes */
int szChange ; /* Size of buffer aChange[] in bytes */
int nChange ; /* Number of changes in buffer aChange[] */
} ;
/*
* * Description of a fuzz change to be applied to a changeset .
*/
struct FuzzChange {
int eType ; /* One of the FUZZ_* constants above */
2018-11-06 20:08:03 +00:00
int iChange ; /* Change or UPDATE to modify */
int iGroup ; /* Group to modify */
int iDelete ; /* Field to remove (FUZZ_COLUMN_DEL) */
2018-11-07 17:52:29 +00:00
u8 * pSub1 ; /* Replace this value with pSub2 */
u8 * pSub2 ; /* And this one with pSub1 */
u8 aSub [ 128 ] ; /* Buffer for substitute value */
2018-11-05 20:37:33 +00:00
int iCurrent ; /* Current change number */
} ;
2018-11-07 17:52:29 +00:00
/*
* * Allocate and return nByte bytes of zeroed memory .
*/
2019-01-08 20:02:48 +00:00
static void * fuzzMalloc ( sqlite3_int64 nByte ) {
void * pRet = sqlite3_malloc64 ( nByte ) ;
2018-11-05 20:37:33 +00:00
if ( pRet ) {
2019-04-01 03:07:21 +00:00
memset ( pRet , 0 , ( size_t ) nByte ) ;
2018-11-05 20:37:33 +00:00
}
return pRet ;
}
2018-11-07 17:52:29 +00:00
/*
* * Free the buffer indicated by the first argument . This function is used
* * to free buffers allocated by fuzzMalloc ( ) .
*/
2018-11-05 20:37:33 +00:00
static void fuzzFree ( void * p ) {
sqlite3_free ( p ) ;
}
2018-11-07 17:52:29 +00:00
/*
* * Argument p points to a buffer containing an SQLite varint that , assuming the
* * input is not corrupt , may be between 0 and 0x7FFFFFFF , inclusive . Before
* * returning , this function sets ( * pnVal ) to the value of that varint , and
* * returns the number of bytes of space that it takes up .
*/
2018-11-05 20:37:33 +00:00
static int fuzzGetVarint ( u8 * p , int * pnVal ) {
int i ;
sqlite3_uint64 nVal = 0 ;
for ( i = 0 ; i < 9 ; i + + ) {
nVal = ( nVal < < 7 ) + ( p [ i ] & 0x7F ) ;
if ( ( p [ i ] & 0x80 ) = = 0 ) {
i + + ;
break ;
}
}
* pnVal = ( int ) nVal ;
return i ;
}
2018-11-07 17:52:29 +00:00
/*
* * Write value nVal into the buffer indicated by argument p as an SQLite
* * varint . nVal is guaranteed to be between 0 and ( 2 ^ 21 - 1 ) , inclusive .
* * Return the number of bytes written to buffer p .
*/
2018-11-05 20:37:33 +00:00
static int fuzzPutVarint ( u8 * p , int nVal ) {
assert ( nVal > 0 & & nVal < 2097152 ) ;
if ( nVal < 128 ) {
2019-04-01 03:07:21 +00:00
p [ 0 ] = ( u8 ) nVal ;
2018-11-05 20:37:33 +00:00
return 1 ;
}
if ( nVal < 16384 ) {
p [ 0 ] = ( ( nVal > > 7 ) & 0x7F ) | 0x80 ;
p [ 1 ] = ( nVal & 0x7F ) ;
return 2 ;
}
p [ 0 ] = ( ( nVal > > 14 ) & 0x7F ) | 0x80 ;
p [ 1 ] = ( ( nVal > > 7 ) & 0x7F ) | 0x80 ;
p [ 2 ] = ( nVal & 0x7F ) ;
return 3 ;
}
/*
* * Read a 64 - bit big - endian integer value from buffer aRec [ ] . Return
* * the value read .
*/
2018-11-07 17:52:29 +00:00
static i64 fuzzGetI64 ( u8 * aRec ) {
return ( i64 ) (
( ( ( u64 ) aRec [ 0 ] ) < < 56 )
+ ( ( ( u64 ) aRec [ 1 ] ) < < 48 )
+ ( ( ( u64 ) aRec [ 2 ] ) < < 40 )
+ ( ( ( u64 ) aRec [ 3 ] ) < < 32 )
+ ( ( ( u64 ) aRec [ 4 ] ) < < 24 )
+ ( ( ( u64 ) aRec [ 5 ] ) < < 16 )
+ ( ( ( u64 ) aRec [ 6 ] ) < < 8 )
+ ( ( ( u64 ) aRec [ 7 ] ) < < 0 )
) ;
2018-11-05 20:37:33 +00:00
}
2018-11-07 17:52:29 +00:00
/*
* * Write value iVal to buffer aRec [ ] as an unsigned 64 - bit big - endian integer .
*/
2018-11-05 20:37:33 +00:00
static void fuzzPutU64 ( u8 * aRec , u64 iVal ) {
aRec [ 0 ] = ( iVal > > 56 ) & 0xFF ;
aRec [ 1 ] = ( iVal > > 48 ) & 0xFF ;
aRec [ 2 ] = ( iVal > > 40 ) & 0xFF ;
aRec [ 3 ] = ( iVal > > 32 ) & 0xFF ;
aRec [ 4 ] = ( iVal > > 24 ) & 0xFF ;
aRec [ 5 ] = ( iVal > > 16 ) & 0xFF ;
aRec [ 6 ] = ( iVal > > 8 ) & 0xFF ;
aRec [ 7 ] = ( iVal ) & 0xFF ;
}
2018-11-07 20:07:28 +00:00
/*
* * Parse a single table - header from the input . Allocate a new change - group
* * object with the results . Return SQLITE_OK if successful , or an error code
* * otherwise .
*/
2018-11-07 17:52:29 +00:00
static int fuzzParseHeader (
2018-11-07 20:07:28 +00:00
FuzzChangeset * pParse , /* Changeset parse object */
u8 * * ppHdr , /* IN/OUT: Iterator */
u8 * pEnd , /* 1 byte past EOF */
FuzzChangesetGroup * * ppGrp /* OUT: New change-group object */
2018-11-07 17:52:29 +00:00
) {
2018-11-05 20:37:33 +00:00
int rc = SQLITE_OK ;
FuzzChangesetGroup * pGrp ;
2018-11-07 17:52:29 +00:00
u8 cHdr = ( pParse - > bPatchset ? ' P ' : ' T ' ) ;
2018-11-05 20:37:33 +00:00
assert ( pEnd > ( * ppHdr ) ) ;
pGrp = ( FuzzChangesetGroup * ) fuzzMalloc ( sizeof ( FuzzChangesetGroup ) ) ;
if ( ! pGrp ) {
rc = SQLITE_NOMEM ;
} else {
u8 * p = * ppHdr ;
2018-11-07 17:52:29 +00:00
if ( p [ 0 ] ! = cHdr ) {
2018-11-05 20:37:33 +00:00
rc = fuzzCorrupt ( ) ;
} else {
p + + ;
p + = fuzzGetVarint ( p , & pGrp - > nCol ) ;
pGrp - > aPK = p ;
p + = pGrp - > nCol ;
pGrp - > zTab = ( const char * ) p ;
2019-04-01 03:07:21 +00:00
p = & p [ strlen ( ( const char * ) p ) + 1 ] ;
2018-11-05 20:37:33 +00:00
if ( p > = pEnd ) {
rc = fuzzCorrupt ( ) ;
}
}
* ppHdr = p ;
}
if ( rc ! = SQLITE_OK ) {
fuzzFree ( pGrp ) ;
pGrp = 0 ;
}
* ppGrp = pGrp ;
return rc ;
}
2018-11-07 20:07:28 +00:00
/*
* * Argument p points to a buffer containing a single changeset - record value .
* * This function attempts to determine the size of the value in bytes . If
* * successful , it sets ( * pSz ) to the size and returns SQLITE_OK . Or , if the
* * buffer does not contain a valid value , SQLITE_CORRUPT is returned and
* * the final value of ( * pSz ) is undefined .
*/
2018-11-05 20:37:33 +00:00
static int fuzzChangeSize ( u8 * p , int * pSz ) {
u8 eType = p [ 0 ] ;
switch ( eType ) {
case 0x00 : /* undefined */
case 0x05 : /* null */
* pSz = 1 ;
break ;
case 0x01 : /* integer */
case 0x02 : /* real */
* pSz = 9 ;
break ;
case 0x03 : /* text */
case 0x04 : { /* blob */
int nTxt ;
int sz ;
sz = fuzzGetVarint ( & p [ 1 ] , & nTxt ) ;
* pSz = 1 + sz + nTxt ;
break ;
}
default :
return fuzzCorrupt ( ) ;
}
return SQLITE_OK ;
}
2018-11-07 20:07:28 +00:00
/*
* * When this function is called , ( * ppRec ) points to the start of a
* * record in a changeset being parsed . This function adds entries
* * to the pParse - > apVal [ ] array for all values and advances ( * ppRec )
* * to one byte past the end of the record . Argument pEnd points to
* * one byte past the end of the input changeset .
* *
* * Argument bPkOnly is true if the record being parsed is part of
* * a DELETE record in a patchset . In this case , all non - primary - key
* * fields have been omitted from the record .
* *
* * SQLITE_OK is returned if successful , or an SQLite error code otherwise .
*/
2018-11-07 17:52:29 +00:00
static int fuzzParseRecord (
u8 * * ppRec , /* IN/OUT: Iterator */
u8 * pEnd , /* One byte after end of input data */
2018-11-07 20:07:28 +00:00
FuzzChangeset * pParse , /* Changeset parse context */
int bPkOnly /* True if non-PK fields omitted */
2018-11-07 17:52:29 +00:00
) {
2018-11-05 20:37:33 +00:00
int rc = SQLITE_OK ;
2018-11-07 17:52:29 +00:00
FuzzChangesetGroup * pGrp = pParse - > apGroup [ pParse - > nGroup - 1 ] ;
2018-11-05 20:37:33 +00:00
int i ;
u8 * p = * ppRec ;
2018-11-08 14:59:51 +00:00
for ( i = 0 ; rc = = SQLITE_OK & & i < pGrp - > nCol ; i + + ) {
2018-11-07 17:52:29 +00:00
if ( bPkOnly = = 0 | | pGrp - > aPK [ i ] ) {
int sz ;
2018-11-08 14:59:51 +00:00
if ( p > = pEnd ) break ;
2018-11-07 17:52:29 +00:00
if ( ( pParse - > nVal & ( pParse - > nVal - 1 ) ) = = 0 ) {
int nNew = pParse - > nVal ? pParse - > nVal * 2 : 4 ;
u8 * * apNew = ( u8 * * ) sqlite3_realloc ( pParse - > apVal , nNew * sizeof ( u8 * ) ) ;
if ( apNew = = 0 ) return SQLITE_NOMEM ;
pParse - > apVal = apNew ;
}
pParse - > apVal [ pParse - > nVal + + ] = p ;
rc = fuzzChangeSize ( p , & sz ) ;
p + = sz ;
2018-11-05 20:37:33 +00:00
}
}
2018-11-07 17:52:29 +00:00
if ( rc = = SQLITE_OK & & i < pGrp - > nCol ) {
2018-11-05 20:37:33 +00:00
rc = fuzzCorrupt ( ) ;
}
* ppRec = p ;
return rc ;
}
2018-11-07 20:07:28 +00:00
/*
* * Parse the array of changes starting at ( * ppData ) and add entries for
* * all values to the pParse - > apVal [ ] array . Argument pEnd points to one byte
* * past the end of the input changeset . If successful , set ( * ppData ) to point
* * to one byte past the end of the change array and return SQLITE_OK .
* * Otherwise , return an SQLite error code . The final value of ( * ppData ) is
* * undefined in this case .
*/
2018-11-05 20:37:33 +00:00
static int fuzzParseChanges ( u8 * * ppData , u8 * pEnd , FuzzChangeset * pParse ) {
2018-11-07 17:52:29 +00:00
u8 cHdr = ( pParse - > bPatchset ? ' P ' : ' T ' ) ;
2018-11-05 20:37:33 +00:00
FuzzChangesetGroup * pGrp = pParse - > apGroup [ pParse - > nGroup - 1 ] ;
int rc = SQLITE_OK ;
u8 * p = * ppData ;
pGrp - > aChange = p ;
2018-11-07 17:52:29 +00:00
while ( rc = = SQLITE_OK & & p < pEnd & & p [ 0 ] ! = cHdr ) {
2018-11-05 20:37:33 +00:00
u8 eOp = p [ 0 ] ;
u8 bIndirect = p [ 1 ] ;
p + = 2 ;
if ( eOp = = SQLITE_UPDATE ) {
pParse - > nUpdate + + ;
2018-11-07 17:52:29 +00:00
if ( pParse - > bPatchset = = 0 ) {
rc = fuzzParseRecord ( & p , pEnd , pParse , 0 ) ;
}
2018-11-05 20:37:33 +00:00
} else if ( eOp ! = SQLITE_INSERT & & eOp ! = SQLITE_DELETE ) {
rc = fuzzCorrupt ( ) ;
}
if ( rc = = SQLITE_OK ) {
2018-11-07 17:52:29 +00:00
int bPkOnly = ( eOp = = SQLITE_DELETE & & pParse - > bPatchset ) ;
rc = fuzzParseRecord ( & p , pEnd , pParse , bPkOnly ) ;
2018-11-05 20:37:33 +00:00
}
pGrp - > nChange + + ;
pParse - > nChange + + ;
}
pGrp - > szChange = p - pGrp - > aChange ;
* ppData = p ;
return rc ;
}
2018-11-07 20:07:28 +00:00
/*
* * Parse the changeset stored in buffer pChangeset ( nChangeset bytes in
* * size ) . If successful , write the results into ( * pParse ) and return
* * SQLITE_OK . Or , if an error occurs , return an SQLite error code . The
* * final state of ( * pParse ) is undefined in this case .
*/
2018-11-05 20:37:33 +00:00
static int fuzzParseChangeset (
u8 * pChangeset , /* Buffer containing changeset */
int nChangeset , /* Size of buffer in bytes */
FuzzChangeset * pParse /* OUT: Results of parse */
) {
u8 * pEnd = & pChangeset [ nChangeset ] ;
u8 * p = pChangeset ;
int rc = SQLITE_OK ;
memset ( pParse , 0 , sizeof ( FuzzChangeset ) ) ;
2018-11-07 17:52:29 +00:00
if ( nChangeset > 0 ) {
pParse - > bPatchset = ( pChangeset [ 0 ] = = ' P ' ) ;
}
2018-11-05 20:37:33 +00:00
while ( rc = = SQLITE_OK & & p < pEnd ) {
FuzzChangesetGroup * pGrp = 0 ;
/* Read a table-header from the changeset */
2018-11-07 17:52:29 +00:00
rc = fuzzParseHeader ( pParse , & p , pEnd , & pGrp ) ;
2018-11-05 20:37:33 +00:00
assert ( ( rc = = SQLITE_OK ) = = ( pGrp ! = 0 ) ) ;
2018-11-07 17:52:29 +00:00
/* If the table-header was successfully parsed, add the new change-group
* * to the array and parse the associated changes . */
2018-11-05 20:37:33 +00:00
if ( rc = = SQLITE_OK ) {
2019-01-08 20:02:48 +00:00
FuzzChangesetGroup * * apNew = ( FuzzChangesetGroup * * ) sqlite3_realloc64 (
2018-11-05 20:37:33 +00:00
pParse - > apGroup , sizeof ( FuzzChangesetGroup * ) * ( pParse - > nGroup + 1 )
) ;
if ( apNew = = 0 ) {
rc = SQLITE_NOMEM ;
} else {
apNew [ pParse - > nGroup ] = pGrp ;
pParse - > apGroup = apNew ;
pParse - > nGroup + + ;
}
rc = fuzzParseChanges ( & p , pEnd , pParse ) ;
}
}
return rc ;
}
2018-11-07 20:07:28 +00:00
/*
* * When this function is called , ( * ppRec ) points to the first byte of
* * a record that is part of change - group pGrp . This function attempts
* * to output a human - readable version of the record to stdout and advance
* * ( * ppRec ) to point to the first byte past the end of the record before
* * returning . If successful , SQLITE_OK is returned . Otherwise , an SQLite
* * error code .
* *
* * If parameter bPkOnly is non - zero , then all non - primary - key fields have
* * been omitted from the record . This occurs for records that are part
* * of DELETE changes in patchsets .
*/
2018-11-07 17:52:29 +00:00
static int fuzzPrintRecord ( FuzzChangesetGroup * pGrp , u8 * * ppRec , int bPKOnly ) {
2018-11-05 20:37:33 +00:00
int rc = SQLITE_OK ;
u8 * p = * ppRec ;
int i ;
const char * zPre = " ( " ;
for ( i = 0 ; i < pGrp - > nCol ; i + + ) {
2018-11-07 17:52:29 +00:00
if ( bPKOnly = = 0 | | pGrp - > aPK [ i ] ) {
u8 eType = p + + [ 0 ] ;
switch ( eType ) {
case 0x00 : /* undefined */
printf ( " %sn/a " , zPre ) ;
break ;
2018-11-05 20:37:33 +00:00
2018-11-07 17:52:29 +00:00
case 0x01 : { /* integer */
sqlite3_int64 iVal = 0 ;
iVal = fuzzGetI64 ( p ) ;
printf ( " %s%lld " , zPre , iVal ) ;
p + = 8 ;
break ;
}
2018-11-05 20:37:33 +00:00
2018-11-07 17:52:29 +00:00
case 0x02 : { /* real */
sqlite3_int64 iVal = 0 ;
double fVal = 0.0 ;
iVal = fuzzGetI64 ( p ) ;
memcpy ( & fVal , & iVal , 8 ) ;
printf ( " %s%f " , zPre , fVal ) ;
p + = 8 ;
break ;
}
2018-11-05 20:37:33 +00:00
2018-11-07 17:52:29 +00:00
case 0x03 : /* text */
case 0x04 : { /* blob */
int nTxt ;
p + = fuzzGetVarint ( p , & nTxt ) ;
printf ( " %s%s " , zPre , eType = = 0x03 ? " ' " : " X' " ) ;
for ( i = 0 ; i < nTxt ; i + + ) {
if ( eType = = 0x03 ) {
printf ( " %c " , p [ i ] ) ;
} else {
char aHex [ 16 ] = { ' 0 ' , ' 1 ' , ' 2 ' , ' 3 ' , ' 4 ' , ' 5 ' , ' 6 ' , ' 7 ' ,
' 8 ' , ' 9 ' , ' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F '
} ;
printf ( " %c " , aHex [ p [ i ] > > 4 ] ) ;
printf ( " %c " , aHex [ p [ i ] & 0x0F ] ) ;
}
2018-11-05 20:37:33 +00:00
}
2018-11-07 17:52:29 +00:00
printf ( " ' " ) ;
p + = nTxt ;
break ;
2018-11-05 20:37:33 +00:00
}
2018-11-07 17:52:29 +00:00
case 0x05 : /* null */
printf ( " %sNULL " , zPre ) ;
break ;
}
zPre = " , " ;
2018-11-05 20:37:33 +00:00
}
}
printf ( " ) " ) ;
* ppRec = p ;
return rc ;
}
2018-11-07 20:07:28 +00:00
/*
* * Print a human - readable version of the table - header and all changes in the
* * change - group passed as the second argument .
*/
static void fuzzPrintGroup ( FuzzChangeset * pParse , FuzzChangesetGroup * pGrp ) {
2018-11-05 20:37:33 +00:00
int i ;
u8 * p ;
/* The table header */
printf ( " TABLE: %s nCol=%d aPK= " , pGrp - > zTab , pGrp - > nCol ) ;
for ( i = 0 ; i < pGrp - > nCol ; i + + ) {
printf ( " %d " , ( int ) pGrp - > aPK [ i ] ) ;
}
printf ( " \n " ) ;
/* The array of changes */
p = pGrp - > aChange ;
for ( i = 0 ; i < pGrp - > nChange ; i + + ) {
u8 eType = p [ 0 ] ;
u8 bIndirect = p [ 1 ] ;
printf ( " %s (ind=%d): " ,
( eType = = SQLITE_INSERT ) ? " INSERT " :
( eType = = SQLITE_DELETE ? " DELETE " : " UPDATE " ) ,
bIndirect
) ;
p + = 2 ;
2018-11-07 17:52:29 +00:00
if ( pParse - > bPatchset = = 0 & & eType = = SQLITE_UPDATE ) {
fuzzPrintRecord ( pGrp , & p , 0 ) ;
2018-11-05 20:37:33 +00:00
}
2018-11-07 17:52:29 +00:00
fuzzPrintRecord ( pGrp , & p , eType = = SQLITE_DELETE & & pParse - > bPatchset ) ;
2018-11-05 20:37:33 +00:00
printf ( " \n " ) ;
}
}
2018-11-07 20:07:28 +00:00
/*
* * Initialize the object passed as the second parameter with details
* * of the change that will be attempted ( type of change , to which part of the
* * changeset it applies etc . ) . If successful , return SQLITE_OK . Or , if an
* * error occurs , return an SQLite error code .
* *
* * If a negative value is returned , then the selected change would have
* * produced a non - well - formed changeset . In this case the caller should
* * call this function again .
*/
2018-11-05 20:37:33 +00:00
static int fuzzSelectChange ( FuzzChangeset * pParse , FuzzChange * pChange ) {
int iSub ;
memset ( pChange , 0 , sizeof ( FuzzChange ) ) ;
2018-11-07 17:52:29 +00:00
pChange - > eType = fuzzRandomInt ( FUZZ_COLUMN_DEL ) + 1 ;
2018-11-05 20:37:33 +00:00
assert ( pChange - > eType = = FUZZ_VALUE_SUB
| | pChange - > eType = = FUZZ_VALUE_MOD
| | pChange - > eType = = FUZZ_VALUE_RND
| | pChange - > eType = = FUZZ_CHANGE_DUP
| | pChange - > eType = = FUZZ_CHANGE_DEL
| | pChange - > eType = = FUZZ_CHANGE_TYPE
| | pChange - > eType = = FUZZ_CHANGE_FIELD
2018-11-06 20:08:03 +00:00
| | pChange - > eType = = FUZZ_CHANGE_INDIRECT
| | pChange - > eType = = FUZZ_GROUP_DUP
| | pChange - > eType = = FUZZ_GROUP_DEL
| | pChange - > eType = = FUZZ_GROUP_SWAP
| | pChange - > eType = = FUZZ_COLUMN_ADD
| | pChange - > eType = = FUZZ_COLUMN_ADDPK
| | pChange - > eType = = FUZZ_COLUMN_DEL
2018-11-05 20:37:33 +00:00
) ;
2018-11-06 20:08:03 +00:00
pChange - > iGroup = fuzzRandomInt ( pParse - > nGroup ) ;
2018-11-05 20:37:33 +00:00
pChange - > iChange = fuzzRandomInt ( pParse - > nChange ) ;
if ( pChange - > eType = = FUZZ_CHANGE_FIELD ) {
if ( pParse - > nUpdate = = 0 ) return - 1 ;
pChange - > iChange = fuzzRandomInt ( pParse - > nUpdate ) ;
}
2018-11-06 20:08:03 +00:00
pChange - > iDelete = - 1 ;
if ( pChange - > eType = = FUZZ_COLUMN_DEL ) {
FuzzChangesetGroup * pGrp = pParse - > apGroup [ pChange - > iGroup ] ;
int i ;
pChange - > iDelete = fuzzRandomInt ( pGrp - > nCol ) ;
for ( i = pGrp - > nCol - 1 ; i > = 0 ; i - - ) {
if ( pGrp - > aPK [ i ] & & pChange - > iDelete ! = i ) break ;
}
if ( i < 0 ) return - 1 ;
}
if ( pChange - > eType = = FUZZ_GROUP_SWAP ) {
FuzzChangesetGroup * pGrp ;
int iGrp = pChange - > iGroup ;
if ( pParse - > nGroup = = 1 ) return - 1 ;
while ( iGrp = = pChange - > iGroup ) {
iGrp = fuzzRandomInt ( pParse - > nGroup ) ;
}
pGrp = pParse - > apGroup [ pChange - > iGroup ] ;
pParse - > apGroup [ pChange - > iGroup ] = pParse - > apGroup [ iGrp ] ;
pParse - > apGroup [ iGrp ] = pGrp ;
}
2018-11-05 20:37:33 +00:00
if ( pChange - > eType = = FUZZ_VALUE_SUB
| | pChange - > eType = = FUZZ_VALUE_MOD
| | pChange - > eType = = FUZZ_VALUE_RND
) {
iSub = fuzzRandomInt ( pParse - > nVal ) ;
2018-11-06 20:08:03 +00:00
pChange - > pSub1 = pParse - > apVal [ iSub ] ;
2018-11-05 20:37:33 +00:00
if ( pChange - > eType = = FUZZ_VALUE_SUB ) {
iSub = fuzzRandomInt ( pParse - > nVal ) ;
2018-11-06 20:08:03 +00:00
pChange - > pSub2 = pParse - > apVal [ iSub ] ;
2018-11-05 20:37:33 +00:00
} else {
pChange - > pSub2 = pChange - > aSub ;
}
if ( pChange - > eType = = FUZZ_VALUE_RND ) {
pChange - > aSub [ 0 ] = ( u8 ) ( fuzzRandomInt ( 5 ) + 1 ) ;
switch ( pChange - > aSub [ 0 ] ) {
case 0x01 : { /* integer */
u64 iVal = fuzzRandomU64 ( ) ;
fuzzPutU64 ( & pChange - > aSub [ 1 ] , iVal ) ;
break ;
}
case 0x02 : { /* real */
u64 iVal1 = fuzzRandomU64 ( ) ;
u64 iVal2 = fuzzRandomU64 ( ) ;
double d = ( double ) iVal1 / ( double ) iVal2 ;
memcpy ( & iVal1 , & d , sizeof ( iVal1 ) ) ;
fuzzPutU64 ( & pChange - > aSub [ 1 ] , iVal1 ) ;
break ;
}
case 0x03 : /* text */
case 0x04 : { /* blob */
int nByte = fuzzRandomInt ( 48 ) ;
2019-04-01 03:07:21 +00:00
pChange - > aSub [ 1 ] = ( u8 ) nByte ;
2018-11-05 20:37:33 +00:00
fuzzRandomBlob ( nByte , & pChange - > aSub [ 2 ] ) ;
if ( pChange - > aSub [ 0 ] = = 0x03 ) {
int i ;
for ( i = 0 ; i < nByte ; i + + ) {
pChange - > aSub [ 2 + i ] & = 0x7F ;
}
}
break ;
}
}
}
if ( pChange - > eType = = FUZZ_VALUE_MOD ) {
int sz ;
int iMod = - 1 ;
fuzzChangeSize ( pChange - > pSub1 , & sz ) ;
memcpy ( pChange - > aSub , pChange - > pSub1 , sz ) ;
switch ( pChange - > aSub [ 0 ] ) {
case 0x01 :
case 0x02 :
iMod = fuzzRandomInt ( 8 ) + 1 ;
break ;
case 0x03 : /* text */
case 0x04 : { /* blob */
int nByte ;
int iFirst = 1 + fuzzGetVarint ( & pChange - > aSub [ 1 ] , & nByte ) ;
if ( nByte > 0 ) {
iMod = fuzzRandomInt ( nByte ) + iFirst ;
}
break ;
}
}
if ( iMod > = 0 ) {
u8 mask = ( 1 < < fuzzRandomInt ( 8 - ( pChange - > aSub [ 0 ] = = 0x03 ) ) ) ;
pChange - > aSub [ iMod ] ^ = mask ;
}
}
}
return SQLITE_OK ;
}
2018-11-07 20:07:28 +00:00
/*
* * Copy a single change from the input to the output changeset , making
* * any modifications specified by ( * pFuzz ) .
*/
2018-11-05 20:37:33 +00:00
static int fuzzCopyChange (
FuzzChangeset * pParse ,
2018-11-06 20:08:03 +00:00
int iGrp ,
2018-11-05 20:37:33 +00:00
FuzzChange * pFuzz ,
u8 * * pp , u8 * * ppOut /* IN/OUT: Input and output pointers */
) {
2018-11-07 17:52:29 +00:00
int bPS = pParse - > bPatchset ;
2018-11-06 20:08:03 +00:00
FuzzChangesetGroup * pGrp = pParse - > apGroup [ iGrp ] ;
2018-11-05 20:37:33 +00:00
u8 * p = * pp ;
u8 * pOut = * ppOut ;
u8 eType = p + + [ 0 ] ;
int iRec ;
2018-11-07 17:52:29 +00:00
int nRec = ( ( eType = = SQLITE_UPDATE & & ! bPS ) ? 2 : 1 ) ;
2018-11-05 20:37:33 +00:00
int iUndef = - 1 ;
2018-11-06 20:08:03 +00:00
int nUpdate = 0 ;
2018-11-05 20:37:33 +00:00
u8 eNew = eType ;
if ( pFuzz - > iCurrent = = pFuzz - > iChange & & pFuzz - > eType = = FUZZ_CHANGE_TYPE ) {
switch ( eType ) {
case SQLITE_INSERT :
eNew = SQLITE_DELETE ;
break ;
case SQLITE_DELETE :
eNew = SQLITE_UPDATE ;
break ;
case SQLITE_UPDATE :
eNew = SQLITE_INSERT ;
break ;
}
}
if ( pFuzz - > iCurrent = = pFuzz - > iChange
& & pFuzz - > eType = = FUZZ_CHANGE_FIELD & & eType = = SQLITE_UPDATE
) {
int sz ;
int i ;
int nDef = 0 ;
u8 * pCsr = p + 1 ;
for ( i = 0 ; i < pGrp - > nCol ; i + + ) {
if ( pCsr [ 0 ] & & pGrp - > aPK [ i ] = = 0 ) nDef + + ;
fuzzChangeSize ( pCsr , & sz ) ;
pCsr + = sz ;
}
if ( nDef < = 1 ) return - 1 ;
nDef = fuzzRandomInt ( nDef ) ;
2018-11-07 17:52:29 +00:00
pCsr = p + 1 ;
2018-11-05 20:37:33 +00:00
for ( i = 0 ; i < pGrp - > nCol ; i + + ) {
if ( pCsr [ 0 ] & & pGrp - > aPK [ i ] = = 0 ) {
if ( nDef = = 0 ) iUndef = i ;
nDef - - ;
}
fuzzChangeSize ( pCsr , & sz ) ;
pCsr + = sz ;
}
}
2018-11-06 20:08:03 +00:00
/* Copy the change type and indirect flag. If the fuzz mode is
* * FUZZ_CHANGE_INDIRECT , and the current change is the one selected for
* * fuzzing , invert the indirect flag . */
2018-11-05 20:37:33 +00:00
* ( pOut + + ) = eNew ;
2018-11-06 20:08:03 +00:00
if ( pFuzz - > eType = = FUZZ_CHANGE_INDIRECT & & pFuzz - > iCurrent = = pFuzz - > iChange ) {
* ( pOut + + ) = ! ( * ( p + + ) ) ;
} else {
* ( pOut + + ) = * ( p + + ) ;
}
2018-11-05 20:37:33 +00:00
for ( iRec = 0 ; iRec < nRec ; iRec + + ) {
int i ;
2018-11-07 17:52:29 +00:00
/* Copy the next record from the output to the input.
*/
2018-11-05 20:37:33 +00:00
for ( i = 0 ; i < pGrp - > nCol ; i + + ) {
int sz ;
u8 * pCopy = p ;
2018-11-07 17:52:29 +00:00
/* If this is a patchset, and the input is a DELETE, then the only
* * fields present are the PK fields . So , if this is not a PK , skip to
* * the next column . If the current fuzz is FUZZ_CHANGE_TYPE , then
* * write a randomly selected value to the output . */
if ( bPS & & eType = = SQLITE_DELETE & & pGrp - > aPK [ i ] = = 0 ) {
if ( eType ! = eNew ) {
assert ( eNew = = SQLITE_UPDATE ) ;
do {
pCopy = pParse - > apVal [ fuzzRandomInt ( pParse - > nVal ) ] ;
} while ( pCopy [ 0 ] = = 0x00 ) ;
fuzzChangeSize ( pCopy , & sz ) ;
memcpy ( pOut , pCopy , sz ) ;
pOut + = sz ;
}
continue ;
}
2018-11-05 20:37:33 +00:00
if ( p = = pFuzz - > pSub1 ) {
pCopy = pFuzz - > pSub2 ;
} else if ( p = = pFuzz - > pSub2 ) {
pCopy = pFuzz - > pSub1 ;
} else if ( i = = iUndef ) {
2019-04-01 03:07:21 +00:00
pCopy = ( u8 * ) " \0 " ;
2018-11-05 20:37:33 +00:00
}
if ( pCopy [ 0 ] = = 0x00 & & eNew ! = eType & & eType = = SQLITE_UPDATE & & iRec = = 0 ) {
while ( pCopy [ 0 ] = = 0x00 ) {
2018-11-06 20:08:03 +00:00
pCopy = pParse - > apVal [ fuzzRandomInt ( pParse - > nVal ) ] ;
2018-11-05 20:37:33 +00:00
}
} else if ( p [ 0 ] = = 0x00 & & pCopy [ 0 ] ! = 0x00 ) {
return - 1 ;
} else {
if ( pGrp - > aPK [ i ] > 0 & & pCopy [ 0 ] = = 0x05 ) return - 1 ;
}
2018-11-07 17:52:29 +00:00
if ( ( pFuzz - > iGroup ! = iGrp | | i ! = pFuzz - > iDelete )
& & ( eNew = = eType | | eType ! = SQLITE_UPDATE | | iRec = = 0 )
& & ( eNew = = eType | | eNew ! = SQLITE_DELETE | | ! bPS | | pGrp - > aPK [ i ] )
) {
fuzzChangeSize ( pCopy , & sz ) ;
memcpy ( pOut , pCopy , sz ) ;
pOut + = sz ;
nUpdate + = ( pGrp - > aPK [ i ] = = 0 & & pCopy [ 0 ] ! = 0x00 ) ;
2018-11-05 20:37:33 +00:00
}
fuzzChangeSize ( p , & sz ) ;
p + = sz ;
}
2018-11-06 20:08:03 +00:00
if ( iGrp = = pFuzz - > iGroup ) {
if ( pFuzz - > eType = = FUZZ_COLUMN_ADD ) {
2018-11-07 17:52:29 +00:00
if ( ! bPS | | eType ! = SQLITE_DELETE ) * ( pOut + + ) = 0x05 ;
2018-11-06 20:08:03 +00:00
} else if ( pFuzz - > eType = = FUZZ_COLUMN_ADDPK ) {
if ( iRec = = 1 ) {
* ( pOut + + ) = 0x00 ;
} else {
u8 * pNew ;
int szNew ;
do {
pNew = pParse - > apVal [ fuzzRandomInt ( pParse - > nVal ) ] ;
} while ( pNew [ 0 ] = = 0x00 | | pNew [ 0 ] = = 0x05 ) ;
fuzzChangeSize ( pNew , & szNew ) ;
memcpy ( pOut , pNew , szNew ) ;
pOut + = szNew ;
}
}
}
2018-11-05 20:37:33 +00:00
}
if ( pFuzz - > iCurrent = = pFuzz - > iChange ) {
if ( pFuzz - > eType = = FUZZ_CHANGE_DUP ) {
int nByte = pOut - ( * ppOut ) ;
memcpy ( pOut , * ppOut , nByte ) ;
pOut + = nByte ;
}
2018-11-06 20:08:03 +00:00
2018-11-05 20:37:33 +00:00
if ( pFuzz - > eType = = FUZZ_CHANGE_DEL ) {
pOut = * ppOut ;
}
2018-11-07 17:52:29 +00:00
if ( eNew ! = eType & & eNew = = SQLITE_UPDATE & & ! bPS ) {
2018-11-05 20:37:33 +00:00
int i ;
u8 * pCsr = ( * ppOut ) + 2 ;
for ( i = 0 ; i < pGrp - > nCol ; i + + ) {
int sz ;
u8 * pCopy = pCsr ;
2019-04-01 03:07:21 +00:00
if ( pGrp - > aPK [ i ] ) pCopy = ( u8 * ) " \0 " ;
2018-11-05 20:37:33 +00:00
fuzzChangeSize ( pCopy , & sz ) ;
memcpy ( pOut , pCopy , sz ) ;
pOut + = sz ;
fuzzChangeSize ( pCsr , & sz ) ;
pCsr + = sz ;
}
}
}
2018-11-06 20:08:03 +00:00
/* If a column is being deleted from this group, and this change was an
* * UPDATE , and there are now no non - PK , non - undefined columns in the
* * change , remove it altogether . */
if ( pFuzz - > eType = = FUZZ_COLUMN_DEL & & pFuzz - > iGroup = = iGrp
& & eType = = SQLITE_UPDATE & & nUpdate = = 0
) {
pOut = * ppOut ;
}
2018-11-05 20:37:33 +00:00
* pp = p ;
* ppOut = pOut ;
pFuzz - > iCurrent + = ( eType = = SQLITE_UPDATE | | pFuzz - > eType ! = FUZZ_CHANGE_FIELD ) ;
return SQLITE_OK ;
}
2018-11-07 20:07:28 +00:00
/*
* * Fuzz the changeset parsed into object pParse and write the results
* * to file zOut on disk . Argument pBuf points to a buffer that is guaranteed
* * to be large enough to hold the fuzzed changeset .
* *
* * Return SQLITE_OK if successful , or an SQLite error code if an error occurs .
*/
2018-11-05 20:37:33 +00:00
static int fuzzDoOneFuzz (
const char * zOut , /* Filename to write modified changeset to */
u8 * pBuf , /* Buffer to use for modified changeset */
FuzzChangeset * pParse /* Parse of input changeset */
) {
FuzzChange change ;
int iGrp ;
int rc = - 1 ;
while ( rc < 0 ) {
u8 * pOut = pBuf ;
rc = fuzzSelectChange ( pParse , & change ) ;
for ( iGrp = 0 ; rc = = SQLITE_OK & & iGrp < pParse - > nGroup ; iGrp + + ) {
FuzzChangesetGroup * pGrp = pParse - > apGroup [ iGrp ] ;
int nTab = strlen ( pGrp - > zTab ) + 1 ;
2018-11-06 20:08:03 +00:00
int j ;
int nRep = 1 ;
/* If this is the group to delete for a FUZZ_GROUP_DEL change, jump to
* * the next group . Unless this is the only group in the changeset - in
* * that case this change cannot be applied .
* *
* * Or , if this is a FUZZ_GROUP_DUP , set nRep to 2 to output two
* * copies of the group . */
if ( change . iGroup = = iGrp ) {
if ( change . eType = = FUZZ_GROUP_DEL ) {
if ( pParse - > nGroup = = 1 ) rc = - 1 ;
continue ;
}
else if ( change . eType = = FUZZ_GROUP_DUP ) {
nRep = 2 ;
}
}
2018-11-05 20:37:33 +00:00
2018-11-06 20:08:03 +00:00
for ( j = 0 ; j < nRep ; j + + ) {
int i ;
u8 * pSaved ;
u8 * p = pGrp - > aChange ;
int nCol = pGrp - > nCol ;
int iPKDel = 0 ;
if ( iGrp = = change . iGroup ) {
if ( change . eType = = FUZZ_COLUMN_ADD
| | change . eType = = FUZZ_COLUMN_ADDPK
) {
nCol + + ;
} else if ( change . eType = = FUZZ_COLUMN_DEL ) {
nCol - - ;
iPKDel = pGrp - > aPK [ change . iDelete ] ;
}
}
/* Output a table header */
2018-11-07 17:52:29 +00:00
pOut + + [ 0 ] = pParse - > bPatchset ? ' P ' : ' T ' ;
2018-11-06 20:08:03 +00:00
pOut + = fuzzPutVarint ( pOut , nCol ) ;
for ( i = 0 ; i < pGrp - > nCol ; i + + ) {
if ( iGrp ! = change . iGroup | | i ! = change . iDelete ) {
u8 v = pGrp - > aPK [ i ] ;
if ( iPKDel & & v > iPKDel ) v - - ;
* ( pOut + + ) = v ;
}
}
if ( nCol > pGrp - > nCol ) {
if ( change . eType = = FUZZ_COLUMN_ADD ) {
* ( pOut + + ) = 0x00 ;
} else {
u8 max = 0 ;
for ( i = 0 ; i < pGrp - > nCol ; i + + ) {
if ( pGrp - > aPK [ i ] > max ) max = pGrp - > aPK [ i ] ;
}
* ( pOut + + ) = max + 1 ;
}
}
memcpy ( pOut , pGrp - > zTab , nTab ) ;
pOut + = nTab ;
/* Output the change array. */
pSaved = pOut ;
for ( i = 0 ; rc = = SQLITE_OK & & i < pGrp - > nChange ; i + + ) {
rc = fuzzCopyChange ( pParse , iGrp , & change , & p , & pOut ) ;
}
if ( pOut = = pSaved ) rc = - 1 ;
2018-11-05 20:37:33 +00:00
}
}
if ( rc = = SQLITE_OK ) {
fuzzWriteFile ( zOut , pBuf , pOut - pBuf ) ;
}
}
return rc ;
}
int main ( int argc , char * * argv ) {
int nRepeat = 0 ; /* Number of output files */
int iSeed = 0 ; /* Value of PRNG seed */
const char * zInput ; /* Name of input file */
void * pChangeset = 0 ; /* Input changeset */
int nChangeset = 0 ; /* Size of input changeset in bytes */
int i ; /* Current output file */
FuzzChangeset changeset ; /* Partially parsed changeset */
int rc ;
u8 * pBuf = 0 ;
if ( argc ! = 4 & & argc ! = 2 ) usage ( argv [ 0 ] ) ;
zInput = argv [ 1 ] ;
fuzzReadFile ( zInput , & nChangeset , & pChangeset ) ;
rc = fuzzParseChangeset ( pChangeset , nChangeset , & changeset ) ;
if ( rc = = SQLITE_OK ) {
if ( argc = = 2 ) {
for ( i = 0 ; i < changeset . nGroup ; i + + ) {
2018-11-07 17:52:29 +00:00
fuzzPrintGroup ( & changeset , changeset . apGroup [ i ] ) ;
2018-11-05 20:37:33 +00:00
}
} else {
2019-01-08 20:02:48 +00:00
pBuf = ( u8 * ) fuzzMalloc ( ( sqlite3_int64 ) nChangeset * 2 + 1024 ) ;
2018-11-05 20:37:33 +00:00
if ( pBuf = = 0 ) {
rc = SQLITE_NOMEM ;
} else {
iSeed = atoi ( argv [ 2 ] ) ;
nRepeat = atoi ( argv [ 3 ] ) ;
fuzzRandomSeed ( ( unsigned int ) iSeed ) ;
for ( i = 0 ; rc = = SQLITE_OK & & i < nRepeat ; i + + ) {
char * zOut = sqlite3_mprintf ( " %s-%d " , zInput , i ) ;
2018-11-06 20:08:03 +00:00
rc = fuzzDoOneFuzz ( zOut , pBuf , & changeset ) ;
2018-11-05 20:37:33 +00:00
sqlite3_free ( zOut ) ;
}
fuzzFree ( pBuf ) ;
}
}
}
if ( rc ! = SQLITE_OK ) {
fprintf ( stderr , " error while processing changeset: %d \n " , rc ) ;
}
2018-11-06 20:08:03 +00:00
2018-11-05 20:37:33 +00:00
return rc ;
}