| /* |
| ** 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. |
| ** |
| ** 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. |
| ** |
| ** 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 |
| ** values. |
| ** |
| ** 7. An UPDATE change that modifies N table columns may be modified so |
| ** that it updates N-1 columns, so long as (N>1). |
| ** |
| ** 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. |
| ** |
| ** 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 |
| ** field is removed from all records. In cases where this leaves an UPDATE |
| ** with no non-PK, non-undefined fields, the entire change is removed. |
| */ |
| |
| #include "sqlite3.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <ctype.h> |
| |
| #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 */ |
| |
| #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 */ |
| |
| |
| |
| 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; |
| sqlite3_int64 sz; |
| 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); |
| sz = ftell(f); |
| rewind(f); |
| pBuf = sqlite3_malloc64( sz ? sz : 1 ); |
| if( pBuf==0 ){ |
| fprintf(stderr, "cannot allocate %d to hold content of \"%s\"\n", |
| (int)sz, zFilename); |
| exit(1); |
| } |
| if( sz>0 ){ |
| if( fread(pBuf, (size_t)sz, 1, f)!=1 ){ |
| fprintf(stderr, "cannot read all %d bytes of \"%s\"\n", |
| (int)sz, zFilename); |
| exit(1); |
| } |
| fclose(f); |
| } |
| *pSz = (int)sz; |
| *ppBuf = pBuf; |
| } |
| |
| /* |
| ** Write the contents of buffer pBuf, size nBuf bytes, into file zFilename |
| ** on disk. zFilename, if it already exists, is clobbered. |
| */ |
| 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 { |
| int bPatchset; /* True for a patchset */ |
| FuzzChangesetGroup **apGroup; /* Array of groups in changeset */ |
| int nGroup; /* Number of items in list pGroup */ |
| u8 **apVal; /* Array of all values in changeset */ |
| int nVal; /* Number of used slots in apVal[] */ |
| int nChange; /* Number of changes in changeset */ |
| int nUpdate; /* Number of UPDATE changes in changeset */ |
| }; |
| |
| /* |
| ** There is one object of this type for each change-group (table header) |
| ** in the input changeset. |
| */ |
| 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 */ |
| int iChange; /* Change or UPDATE to modify */ |
| int iGroup; /* Group to modify */ |
| int iDelete; /* Field to remove (FUZZ_COLUMN_DEL) */ |
| u8 *pSub1; /* Replace this value with pSub2 */ |
| u8 *pSub2; /* And this one with pSub1 */ |
| u8 aSub[128]; /* Buffer for substitute value */ |
| int iCurrent; /* Current change number */ |
| }; |
| |
| /* |
| ** Allocate and return nByte bytes of zeroed memory. |
| */ |
| static void *fuzzMalloc(sqlite3_int64 nByte){ |
| void *pRet = sqlite3_malloc64(nByte); |
| if( pRet ){ |
| memset(pRet, 0, (size_t)nByte); |
| } |
| return pRet; |
| } |
| |
| /* |
| ** Free the buffer indicated by the first argument. This function is used |
| ** to free buffers allocated by fuzzMalloc(). |
| */ |
| static void fuzzFree(void *p){ |
| sqlite3_free(p); |
| } |
| |
| /* |
| ** 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. |
| */ |
| 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; |
| } |
| |
| /* |
| ** 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. |
| */ |
| static int fuzzPutVarint(u8 *p, int nVal){ |
| assert( nVal>0 && nVal<2097152 ); |
| if( nVal<128 ){ |
| p[0] = (u8)nVal; |
| 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. |
| */ |
| 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) |
| ); |
| } |
| |
| /* |
| ** Write value iVal to buffer aRec[] as an unsigned 64-bit big-endian integer. |
| */ |
| 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; |
| } |
| |
| /* |
| ** 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. |
| */ |
| static int fuzzParseHeader( |
| FuzzChangeset *pParse, /* Changeset parse object */ |
| u8 **ppHdr, /* IN/OUT: Iterator */ |
| u8 *pEnd, /* 1 byte past EOF */ |
| FuzzChangesetGroup **ppGrp /* OUT: New change-group object */ |
| ){ |
| int rc = SQLITE_OK; |
| FuzzChangesetGroup *pGrp; |
| u8 cHdr = (pParse->bPatchset ? 'P' : 'T'); |
| |
| assert( pEnd>(*ppHdr) ); |
| pGrp = (FuzzChangesetGroup*)fuzzMalloc(sizeof(FuzzChangesetGroup)); |
| if( !pGrp ){ |
| rc = SQLITE_NOMEM; |
| }else{ |
| u8 *p = *ppHdr; |
| if( p[0]!=cHdr ){ |
| rc = fuzzCorrupt(); |
| }else{ |
| p++; |
| p += fuzzGetVarint(p, &pGrp->nCol); |
| pGrp->aPK = p; |
| p += pGrp->nCol; |
| pGrp->zTab = (const char*)p; |
| p = &p[strlen((const char*)p)+1]; |
| |
| if( p>=pEnd ){ |
| rc = fuzzCorrupt(); |
| } |
| } |
| *ppHdr = p; |
| } |
| |
| if( rc!=SQLITE_OK ){ |
| fuzzFree(pGrp); |
| pGrp = 0; |
| } |
| |
| *ppGrp = pGrp; |
| return rc; |
| } |
| |
| /* |
| ** 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. |
| */ |
| 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; |
| } |
| |
| /* |
| ** 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. |
| */ |
| static int fuzzParseRecord( |
| u8 **ppRec, /* IN/OUT: Iterator */ |
| u8 *pEnd, /* One byte after end of input data */ |
| FuzzChangeset *pParse, /* Changeset parse context */ |
| int bPkOnly /* True if non-PK fields omitted */ |
| ){ |
| int rc = SQLITE_OK; |
| FuzzChangesetGroup *pGrp = pParse->apGroup[pParse->nGroup-1]; |
| int i; |
| u8 *p = *ppRec; |
| |
| for(i=0; rc==SQLITE_OK && i<pGrp->nCol; i++){ |
| if( bPkOnly==0 || pGrp->aPK[i] ){ |
| int sz; |
| if( p>=pEnd ) break; |
| 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; |
| } |
| } |
| |
| if( rc==SQLITE_OK && i<pGrp->nCol ){ |
| rc = fuzzCorrupt(); |
| } |
| |
| *ppRec = p; |
| return rc; |
| } |
| |
| /* |
| ** 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. |
| */ |
| static int fuzzParseChanges(u8 **ppData, u8 *pEnd, FuzzChangeset *pParse){ |
| u8 cHdr = (pParse->bPatchset ? 'P' : 'T'); |
| FuzzChangesetGroup *pGrp = pParse->apGroup[pParse->nGroup-1]; |
| int rc = SQLITE_OK; |
| u8 *p = *ppData; |
| |
| pGrp->aChange = p; |
| while( rc==SQLITE_OK && p<pEnd && p[0]!=cHdr ){ |
| u8 eOp = p[0]; |
| u8 bIndirect = p[1]; |
| |
| p += 2; |
| if( eOp==SQLITE_UPDATE ){ |
| pParse->nUpdate++; |
| if( pParse->bPatchset==0 ){ |
| rc = fuzzParseRecord(&p, pEnd, pParse, 0); |
| } |
| }else if( eOp!=SQLITE_INSERT && eOp!=SQLITE_DELETE ){ |
| rc = fuzzCorrupt(); |
| } |
| if( rc==SQLITE_OK ){ |
| int bPkOnly = (eOp==SQLITE_DELETE && pParse->bPatchset); |
| rc = fuzzParseRecord(&p, pEnd, pParse, bPkOnly); |
| } |
| pGrp->nChange++; |
| pParse->nChange++; |
| } |
| pGrp->szChange = p - pGrp->aChange; |
| |
| *ppData = p; |
| return rc; |
| } |
| |
| /* |
| ** 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. |
| */ |
| 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)); |
| if( nChangeset>0 ){ |
| pParse->bPatchset = (pChangeset[0]=='P'); |
| } |
| |
| while( rc==SQLITE_OK && p<pEnd ){ |
| FuzzChangesetGroup *pGrp = 0; |
| |
| /* Read a table-header from the changeset */ |
| rc = fuzzParseHeader(pParse, &p, pEnd, &pGrp); |
| assert( (rc==SQLITE_OK)==(pGrp!=0) ); |
| |
| /* If the table-header was successfully parsed, add the new change-group |
| ** to the array and parse the associated changes. */ |
| if( rc==SQLITE_OK ){ |
| FuzzChangesetGroup **apNew = (FuzzChangesetGroup**)sqlite3_realloc64( |
| 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; |
| } |
| |
| /* |
| ** 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. |
| */ |
| static int fuzzPrintRecord(FuzzChangesetGroup *pGrp, u8 **ppRec, int bPKOnly){ |
| int rc = SQLITE_OK; |
| u8 *p = *ppRec; |
| int i; |
| const char *zPre = " ("; |
| |
| for(i=0; i<pGrp->nCol; i++){ |
| if( bPKOnly==0 || pGrp->aPK[i] ){ |
| u8 eType = p++[0]; |
| switch( eType ){ |
| case 0x00: /* undefined */ |
| printf("%sn/a", zPre); |
| break; |
| |
| case 0x01: { /* integer */ |
| sqlite3_int64 iVal = 0; |
| iVal = fuzzGetI64(p); |
| printf("%s%lld", zPre, iVal); |
| p += 8; |
| break; |
| } |
| |
| 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; |
| } |
| |
| 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 ]); |
| } |
| } |
| printf("'"); |
| p += nTxt; |
| break; |
| } |
| |
| case 0x05: /* null */ |
| printf("%sNULL", zPre); |
| break; |
| } |
| zPre = ", "; |
| } |
| } |
| printf(")"); |
| |
| *ppRec = p; |
| return rc; |
| } |
| |
| /* |
| ** 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){ |
| 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; |
| |
| if( pParse->bPatchset==0 && eType==SQLITE_UPDATE ){ |
| fuzzPrintRecord(pGrp, &p, 0); |
| } |
| fuzzPrintRecord(pGrp, &p, eType==SQLITE_DELETE && pParse->bPatchset); |
| printf("\n"); |
| } |
| } |
| |
| /* |
| ** 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. |
| */ |
| static int fuzzSelectChange(FuzzChangeset *pParse, FuzzChange *pChange){ |
| int iSub; |
| |
| memset(pChange, 0, sizeof(FuzzChange)); |
| pChange->eType = fuzzRandomInt(FUZZ_COLUMN_DEL) + 1; |
| |
| 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 |
| || 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 |
| ); |
| |
| pChange->iGroup = fuzzRandomInt(pParse->nGroup); |
| pChange->iChange = fuzzRandomInt(pParse->nChange); |
| if( pChange->eType==FUZZ_CHANGE_FIELD ){ |
| if( pParse->nUpdate==0 ) return -1; |
| pChange->iChange = fuzzRandomInt(pParse->nUpdate); |
| } |
| |
| 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; |
| } |
| |
| if( pChange->eType==FUZZ_VALUE_SUB |
| || pChange->eType==FUZZ_VALUE_MOD |
| || pChange->eType==FUZZ_VALUE_RND |
| ){ |
| iSub = fuzzRandomInt(pParse->nVal); |
| pChange->pSub1 = pParse->apVal[iSub]; |
| if( pChange->eType==FUZZ_VALUE_SUB ){ |
| iSub = fuzzRandomInt(pParse->nVal); |
| pChange->pSub2 = pParse->apVal[iSub]; |
| }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); |
| pChange->aSub[1] = (u8)nByte; |
| 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; |
| } |
| |
| /* |
| ** Copy a single change from the input to the output changeset, making |
| ** any modifications specified by (*pFuzz). |
| */ |
| static int fuzzCopyChange( |
| FuzzChangeset *pParse, |
| int iGrp, |
| FuzzChange *pFuzz, |
| u8 **pp, u8 **ppOut /* IN/OUT: Input and output pointers */ |
| ){ |
| int bPS = pParse->bPatchset; |
| FuzzChangesetGroup *pGrp = pParse->apGroup[iGrp]; |
| u8 *p = *pp; |
| u8 *pOut = *ppOut; |
| u8 eType = p++[0]; |
| int iRec; |
| int nRec = ((eType==SQLITE_UPDATE && !bPS) ? 2 : 1); |
| int iUndef = -1; |
| int nUpdate = 0; |
| |
| 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); |
| pCsr = p+1; |
| 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; |
| } |
| } |
| |
| /* 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. */ |
| *(pOut++) = eNew; |
| if( pFuzz->eType==FUZZ_CHANGE_INDIRECT && pFuzz->iCurrent==pFuzz->iChange ){ |
| *(pOut++) = !(*(p++)); |
| }else{ |
| *(pOut++) = *(p++); |
| } |
| |
| for(iRec=0; iRec<nRec; iRec++){ |
| int i; |
| |
| /* Copy the next record from the output to the input. |
| */ |
| for(i=0; i<pGrp->nCol; i++){ |
| int sz; |
| u8 *pCopy = p; |
| |
| /* 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; |
| } |
| |
| if( p==pFuzz->pSub1 ){ |
| pCopy = pFuzz->pSub2; |
| }else if( p==pFuzz->pSub2 ){ |
| pCopy = pFuzz->pSub1; |
| }else if( i==iUndef ){ |
| pCopy = (u8*)"\0"; |
| } |
| |
| if( pCopy[0]==0x00 && eNew!=eType && eType==SQLITE_UPDATE && iRec==0 ){ |
| while( pCopy[0]==0x00 ){ |
| pCopy = pParse->apVal[fuzzRandomInt(pParse->nVal)]; |
| } |
| }else if( p[0]==0x00 && pCopy[0]!=0x00 ){ |
| return -1; |
| }else{ |
| if( pGrp->aPK[i]>0 && pCopy[0]==0x05 ) return -1; |
| } |
| |
| 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); |
| } |
| |
| fuzzChangeSize(p, &sz); |
| p += sz; |
| } |
| |
| if( iGrp==pFuzz->iGroup ){ |
| if( pFuzz->eType==FUZZ_COLUMN_ADD ){ |
| if( !bPS || eType!=SQLITE_DELETE ) *(pOut++) = 0x05; |
| }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; |
| } |
| } |
| } |
| } |
| |
| if( pFuzz->iCurrent==pFuzz->iChange ){ |
| if( pFuzz->eType==FUZZ_CHANGE_DUP ){ |
| int nByte = pOut - (*ppOut); |
| memcpy(pOut, *ppOut, nByte); |
| pOut += nByte; |
| } |
| |
| if( pFuzz->eType==FUZZ_CHANGE_DEL ){ |
| pOut = *ppOut; |
| } |
| if( eNew!=eType && eNew==SQLITE_UPDATE && !bPS ){ |
| int i; |
| u8 *pCsr = (*ppOut) + 2; |
| for(i=0; i<pGrp->nCol; i++){ |
| int sz; |
| u8 *pCopy = pCsr; |
| if( pGrp->aPK[i] ) pCopy = (u8*)"\0"; |
| fuzzChangeSize(pCopy, &sz); |
| memcpy(pOut, pCopy, sz); |
| pOut += sz; |
| fuzzChangeSize(pCsr, &sz); |
| pCsr += sz; |
| } |
| } |
| } |
| |
| /* 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; |
| } |
| |
| *pp = p; |
| *ppOut = pOut; |
| pFuzz->iCurrent += (eType==SQLITE_UPDATE || pFuzz->eType!=FUZZ_CHANGE_FIELD); |
| return SQLITE_OK; |
| } |
| |
| /* |
| ** 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. |
| */ |
| 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; |
| 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; |
| } |
| } |
| |
| 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 */ |
| pOut++[0] = pParse->bPatchset ? 'P' : 'T'; |
| 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; |
| } |
| } |
| 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++){ |
| fuzzPrintGroup(&changeset, changeset.apGroup[i]); |
| } |
| }else{ |
| pBuf = (u8*)fuzzMalloc((sqlite3_int64)nChangeset*2 + 1024); |
| 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); |
| rc = fuzzDoOneFuzz(zOut, pBuf, &changeset); |
| sqlite3_free(zOut); |
| } |
| fuzzFree(pBuf); |
| } |
| } |
| } |
| |
| if( rc!=SQLITE_OK ){ |
| fprintf(stderr, "error while processing changeset: %d\n", rc); |
| } |
| |
| return rc; |
| } |