]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add preliminary version of "changesetfuzz" program. For fuzzing changeset data
authordan <dan@noemail.net>
Mon, 5 Nov 2018 20:37:33 +0000 (20:37 +0000)
committerdan <dan@noemail.net>
Mon, 5 Nov 2018 20:37:33 +0000 (20:37 +0000)
without creating corrupt changesets.

FossilOrigin-Name: 81ac8745faac0bda8d68ac113f1938f0e25208642e8ceb2af452680086454cb5

Makefile.in
Makefile.msc
ext/session/changesetfuzz.c [new file with mode: 0644]
main.mk
manifest
manifest.uuid

index 434a447f6ce6c9b2e31283af8599419a858a6f04..826a10e4e7f73c3294afa93d01e4f2b6f01d3991 100644 (file)
@@ -1298,6 +1298,9 @@ showshm$(TEXE):   $(TOP)/tool/showshm.c
 changeset$(TEXE):      $(TOP)/ext/session/changeset.c sqlite3.lo
        $(LTLINK) -o $@ $(TOP)/ext/session/changeset.c sqlite3.lo $(TLIBS)
 
+changesetfuzz$(TEXE):  $(TOP)/ext/session/changesetfuzz.c sqlite3.lo
+       $(LTLINK) -o $@ $(TOP)/ext/session/changesetfuzz.c sqlite3.lo $(TLIBS)
+
 rollback-test$(TEXE):  $(TOP)/tool/rollback-test.c sqlite3.lo
        $(LTLINK) -o $@ $(TOP)/tool/rollback-test.c sqlite3.lo $(TLIBS)
 
index e8b669283d1d80b413ea4ab0c73848f824965ed6..8cc51a20349d9f0b9f6fcb98ebf47964102a512f 100644 (file)
@@ -2448,6 +2448,11 @@ changeset.exe:   $(TOP)\ext\session\changeset.c $(SQLITE3C) $(SQLITE3H)
                -DSQLITE_ENABLE_SESSION=1 -DSQLITE_ENABLE_PREUPDATE_HOOK=1 \
                $(TOP)\ext\session\changeset.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
 
+changesetfuzz.exe:     $(TOP)\ext\session\changesetfuzz.c $(SQLITE3C) $(SQLITE3H)
+       $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
+               -DSQLITE_ENABLE_SESSION=1 -DSQLITE_ENABLE_PREUPDATE_HOOK=1 \
+               $(TOP)\ext\session\changesetfuzz.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+
 fts3view.exe:  $(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C) $(SQLITE3H)
        $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
                $(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
diff --git a/ext/session/changesetfuzz.c b/ext/session/changesetfuzz.c
new file mode 100644 (file)
index 0000000..c430ab4
--- /dev/null
@@ -0,0 +1,932 @@
+/*
+** 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.
+**
+** 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).
+**
+*/
+
+#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
+#define FUZZ_CHANGE_DEL    5
+#define FUZZ_CHANGE_TYPE   6
+#define FUZZ_CHANGE_FIELD  7
+
+#if 0
+#define FUZZ_COLUMN_ADD    1      /* Add column to table definition */
+#define FUZZ_COLUMN_DEL    2      /* Remove column from table definition */
+#define FUZZ_PK_ADD        3      /* Add a PK column */
+#define FUZZ_PK_DEL        4      /* Delete a PK column */
+#define FUZZ_NAME_CHANGE   5      /* Change a table name */
+#endif
+
+
+
+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;
+  int 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 = (int)ftell(f);
+  rewind(f);
+  pBuf = sqlite3_malloc( sz ? sz : 1 );
+  if( pBuf==0 ){
+    fprintf(stderr, "cannot allocate %d to hold content of \"%s\"\n",
+            sz, zFilename);
+    exit(1);
+  }
+  if( sz>0 ){
+    if( fread(pBuf, sz, 1, f)!=1 ){
+      fprintf(stderr, "cannot read all %d bytes of \"%s\"\n", sz, zFilename);
+      exit(1);
+    }
+    fclose(f);
+  }
+  *pSz = sz;
+  *ppBuf = pBuf;
+}
+
+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;
+
+#define FUZZER_AVAL_SZ 512
+
+/* 
+** Object containing partially parsed changeset.
+*/
+struct FuzzChangeset {
+  FuzzChangesetGroup **apGroup;   /* Array of groups in changeset */
+  int nGroup;                     /* Number of items in list pGroup */
+  u8 *aVal[FUZZER_AVAL_SZ];       /* Array of first few values in changeset */
+  int nVal;                       /* Number of used slots in aVal[] */
+  int nChange;                    /* Number of changes in changeset */
+  int nUpdate;                    /* Number of UPDATE changes in 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[] */
+  FuzzChangesetGroup *pNextGroup;
+};
+
+/*
+** Description of a fuzz change to be applied to a changeset.
+*/
+struct FuzzChange {
+  int eType;                      /* One of the FUZZ_* constants above */
+  int iChange;                    /* Change to modify */
+  u8 *pSub1;
+  u8 *pSub2;
+  u8 aSub[128];                   /* Substitute value */
+
+  int iCurrent;                   /* Current change number */
+};
+
+static void *fuzzMalloc(int nByte){
+  void *pRet = sqlite3_malloc(nByte);
+  if( pRet ){
+    memset(pRet, 0, nByte);
+  }
+  return pRet;
+}
+
+static void fuzzFree(void *p){
+  sqlite3_free(p);
+}
+
+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;
+}
+
+static int fuzzPutVarint(u8 *p, int nVal){
+  assert( nVal>0 && nVal<2097152 );
+  if( nVal<128 ){
+    p[0] = 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;
+}
+
+/* Load an unaligned and unsigned 32-bit integer */
+#define FUZZ_UINT32(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3])
+
+/*
+** Read a 64-bit big-endian integer value from buffer aRec[]. Return
+** the value read.
+*/
+static sqlite3_int64 fuzzGetI64(u8 *aRec){
+  u64 x = FUZZ_UINT32(aRec);
+  u32 y = FUZZ_UINT32(aRec+4);
+  x = (x<<32) + y;
+  return (sqlite3_int64)x;
+}
+
+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;
+}
+
+static int fuzzParseHeader(u8 **ppHdr, u8 *pEnd, FuzzChangesetGroup **ppGrp){
+  int rc = SQLITE_OK;
+  FuzzChangesetGroup *pGrp;
+
+  assert( pEnd>(*ppHdr) );
+  pGrp = (FuzzChangesetGroup*)fuzzMalloc(sizeof(FuzzChangesetGroup));
+  if( !pGrp ){
+    rc = SQLITE_NOMEM;
+  }else{
+    u8 *p = *ppHdr;
+    if( p[0]!='T' ){
+      rc = fuzzCorrupt();
+    }else{
+      p++;
+      p += fuzzGetVarint(p, &pGrp->nCol);
+      pGrp->aPK = p;
+      p += pGrp->nCol;
+      pGrp->zTab = (const char*)p;
+      p = &p[strlen(p)+1];
+
+      if( p>=pEnd ){
+        rc = fuzzCorrupt();
+      }
+    }
+    *ppHdr = p;
+  }
+
+  if( rc!=SQLITE_OK ){
+    fuzzFree(pGrp);
+    pGrp = 0;
+  }
+
+  *ppGrp = pGrp;
+  return rc;
+}
+
+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;
+}
+
+static int fuzzParseRecord(u8 **ppRec, u8 *pEnd, FuzzChangeset *pParse){
+  int rc = SQLITE_OK;
+  int nCol = pParse->apGroup[pParse->nGroup-1]->nCol;
+  int i;
+  u8 *p = *ppRec;
+
+  for(i=0; rc==SQLITE_OK && i<nCol && p<pEnd; i++){
+    int sz;
+    if( pParse->nVal<FUZZER_AVAL_SZ ){
+      pParse->aVal[pParse->nVal++] = p;
+    }
+    rc = fuzzChangeSize(p, &sz);
+    p += sz;
+  }
+
+  if( rc==SQLITE_OK && i<nCol ){
+    rc = fuzzCorrupt();
+  }
+
+  *ppRec = p;
+  return rc;
+}
+
+static int fuzzParseChanges(u8 **ppData, u8 *pEnd, FuzzChangeset *pParse){
+  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]!='T' ){
+    u8 eOp = p[0];
+    u8 bIndirect = p[1];
+
+    p += 2;
+    if( eOp==SQLITE_UPDATE ){
+      pParse->nUpdate++;
+      rc = fuzzParseRecord(&p, pEnd, pParse);
+    }else if( eOp!=SQLITE_INSERT && eOp!=SQLITE_DELETE ){
+      rc = fuzzCorrupt();
+    }
+    if( rc==SQLITE_OK ){
+      rc = fuzzParseRecord(&p, pEnd, pParse);
+    }
+    pGrp->nChange++;
+    pParse->nChange++;
+  }
+  pGrp->szChange = p - pGrp->aChange;
+
+  *ppData = p;
+  return rc;
+}
+
+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));
+
+  while( rc==SQLITE_OK && p<pEnd ){
+    FuzzChangesetGroup *pGrp = 0;
+
+    /* Read a table-header from the changeset */
+    rc = fuzzParseHeader(&p, pEnd, &pGrp);
+    assert( (rc==SQLITE_OK)==(pGrp!=0) );
+
+    /* If the table-header was successfully parsed, link the new change-group
+    ** into the linked list and parse the associated array of changes. */
+    if( rc==SQLITE_OK ){
+      FuzzChangesetGroup **apNew = (FuzzChangesetGroup**)sqlite3_realloc(
+          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;
+}
+
+static int fuzzPrintRecord(FuzzChangesetGroup *pGrp, u8 **ppRec){
+  int rc = SQLITE_OK;
+  u8 *p = *ppRec;
+  int i;
+  const char *zPre = " (";
+
+  for(i=0; i<pGrp->nCol; 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;
+        int sz;
+        int i;
+        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;
+}
+
+static int fuzzPrintGroup(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( eType==SQLITE_UPDATE ){
+      fuzzPrintRecord(pGrp, &p);
+    }
+    fuzzPrintRecord(pGrp, &p);
+    printf("\n");
+  }
+}
+
+static int fuzzSelectChange(FuzzChangeset *pParse, FuzzChange *pChange){
+  int iSub;
+
+  memset(pChange, 0, sizeof(FuzzChange));
+  pChange->eType = fuzzRandomInt(7) + FUZZ_VALUE_SUB;
+
+  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->iChange = fuzzRandomInt(pParse->nChange);
+  if( pChange->eType==FUZZ_CHANGE_FIELD ){
+    if( pParse->nUpdate==0 ) return -1;
+    pChange->iChange = fuzzRandomInt(pParse->nUpdate);
+  }
+
+  if( pChange->eType==FUZZ_VALUE_SUB 
+   || pChange->eType==FUZZ_VALUE_MOD 
+   || pChange->eType==FUZZ_VALUE_RND 
+  ){
+    iSub = fuzzRandomInt(pParse->nVal);
+    pChange->pSub1 = pParse->aVal[iSub];
+    if( pChange->eType==FUZZ_VALUE_SUB ){
+      iSub = fuzzRandomInt(pParse->nVal);
+      pChange->pSub2 = pParse->aVal[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] = 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;
+}
+
+static int fuzzCopyChange(
+  FuzzChangeset *pParse,
+  FuzzChangesetGroup *pGrp, 
+  FuzzChange *pFuzz,
+  u8 **pp, u8 **ppOut             /* IN/OUT: Input and output pointers */
+){
+  u8 *p = *pp;
+  u8 *pOut = *ppOut;
+  u8 eType = p++[0];
+  int iRec;
+  int nRec = (eType==SQLITE_UPDATE ? 2 : 1);
+  int iUndef = -1;
+
+  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);
+    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 */
+  *(pOut++) = eNew;
+  *(pOut++) = *(p++);
+  for(iRec=0; iRec<nRec; iRec++){
+    int i;
+    for(i=0; i<pGrp->nCol; i++){
+      int sz;
+      u8 *pCopy = p;
+
+      if( p==pFuzz->pSub1 ){
+        pCopy = pFuzz->pSub2;
+      }else if( p==pFuzz->pSub2 ){
+        pCopy = pFuzz->pSub1;
+      }else if( i==iUndef ){
+        pCopy = "\0";
+      }
+
+      if( pCopy[0]==0x00 && eNew!=eType && eType==SQLITE_UPDATE && iRec==0 ){
+        while( pCopy[0]==0x00 ){
+          pCopy = pParse->aVal[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( eNew==eType || eType!=SQLITE_UPDATE || iRec==0 ){
+        fuzzChangeSize(pCopy, &sz);
+        memcpy(pOut, pCopy, sz);
+        pOut += sz;
+      }
+
+      fuzzChangeSize(p, &sz);
+      p += sz;
+    }
+  }
+
+  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 ){
+      if( pGrp->nChange==1 ) return -1;
+      pOut = *ppOut;
+    }
+    if( eNew!=eType && eNew==SQLITE_UPDATE ){
+      int i;
+      u8 *pCsr = (*ppOut) + 2;
+      for(i=0; i<pGrp->nCol; i++){
+        int sz;
+        u8 *pCopy = pCsr;
+        if( pGrp->aPK[i] ) pCopy = "\0";
+        fuzzChangeSize(pCopy, &sz);
+        memcpy(pOut, pCopy, sz);
+        pOut += sz;
+        fuzzChangeSize(pCsr, &sz);
+        pCsr += sz;
+      }
+    }
+  }
+
+  *pp = p;
+  *ppOut = pOut;
+  pFuzz->iCurrent += (eType==SQLITE_UPDATE || pFuzz->eType!=FUZZ_CHANGE_FIELD);
+  return SQLITE_OK;
+}
+
+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;
+      u8 *p;
+      int i;
+
+      /* Output a table header */
+      pOut++[0] = 'T';
+      pOut += fuzzPutVarint(pOut, pGrp->nCol);
+      memcpy(pOut, pGrp->aPK, pGrp->nCol);
+      pOut += pGrp->nCol;
+      memcpy(pOut, pGrp->zTab, nTab);
+      pOut += nTab;
+
+      /* Output the change array */
+      p = pGrp->aChange;
+      for(i=0; rc==SQLITE_OK && i<pGrp->nChange; i++){
+        rc = fuzzCopyChange(pParse, pGrp, &change, &p, &pOut);
+      }
+    }
+    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.apGroup[i]);
+      }
+    }else{
+      pBuf = (u8*)fuzzMalloc(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);
+          fuzzDoOneFuzz(zOut, pBuf, &changeset);
+          sqlite3_free(zOut);
+        }
+        fuzzFree(pBuf);
+      }
+    }
+  }
+
+  if( rc!=SQLITE_OK ){
+    fprintf(stderr, "error while processing changeset: %d\n", rc);
+  }
+  return rc;
+}
+
diff --git a/main.mk b/main.mk
index b18c444d6ba57547208edeec973aab8addbad2bf..52cc3761eb3442e0f230135802f3683901e3ed3c 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -998,6 +998,10 @@ changeset$(EXE):   $(TOP)/ext/session/changeset.c sqlite3.o
        $(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o changeset$(EXE) \
                $(TOP)/ext/session/changeset.c sqlite3.o $(THREADLIB)
 
+changesetfuzz$(EXE):   $(TOP)/ext/session/changesetfuzz.c sqlite3.o
+       $(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o changesetfuzz$(EXE) \
+               $(TOP)/ext/session/changesetfuzz.c sqlite3.o $(THREADLIB)
+
 fts3view$(EXE):        $(TOP)/ext/fts3/tool/fts3view.c sqlite3.o
        $(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o fts3view$(EXE) \
                $(TOP)/ext/fts3/tool/fts3view.c sqlite3.o $(THREADLIB)
index d225e86d8618b4566ba9fad470a6e50f949c34cd..f7a39c9f657b57c5967ccb01b66be94c77e5300e 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,10 +1,10 @@
-C Add\sthe\sSQLITE_DBCONFIG_DEFENSIVE\sflag.
-D 2018-11-05T19:37:30.613
+C Add\spreliminary\sversion\sof\s"changesetfuzz"\sprogram.\sFor\sfuzzing\schangeset\sdata\nwithout\screating\scorrupt\schangesets.
+D 2018-11-05T20:37:33.019
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
-F Makefile.in edbb6e20bb1decf65f6c64c9e61004a69bdf8afb39cdce5337c916b03dfcd1e3
+F Makefile.in d298b31769d4c737887102462cd45684786b09f2a626a80b3e413790fb436219
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
-F Makefile.msc 5402eae167757dae275cd99c21d21d77db2ec1877c58b4f790ef747a60b95c9f
+F Makefile.msc 071853dfc2148aadaeb7a153b9986cc9d71b7f256874f79b119f97a4c89453f5
 F README.md 377233394b905d3b2e2b33741289e093bc93f2e7adbe00923b2c5958c9a9edee
 F VERSION 654da1d4053fb09ffc33a3910e6d427182a7dcdc67e934fa83de2849ac83fccb
 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@@ -391,6 +391,7 @@ F ext/rtree/util/randomshape.tcl 54ee03d0d4a1c621806f7f44d5b78d2db8fac26e0e8687c
 F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
 F ext/rtree/visual01.txt e9c2564083bcd30ec51b07f881bffbf0e12b50a3f6fced0c222c5c1d2f94ac66
 F ext/session/changeset.c 4ccbaa4531944c24584bf6a61ba3a39c62b6267a
+F ext/session/changesetfuzz.c 1e56326c4b76a6f1802ca73629409ce1eb10d60a388a43d8e7c74b33b1f4725c
 F ext/session/session1.test 0b2f88995832ea040ae8e83a1ad4afa99c00b85c779d213da73a95ea4113233e
 F ext/session/session2.test 284de45abae4cc1082bc52012ee81521d5ac58e0
 F ext/session/session3.test ce9ce3dfa489473987f899e9f6a0f2db9bde3479
@@ -426,7 +427,7 @@ F ext/userauth/userauth.c f81aa5a3ecacf406f170c62a144405858f6f6de51dbdc0920134e6
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk 71a3471e99c18bf5ddb9593e5ec3ecef858e97551b949401f8cff2aaba79c67c
+F main.mk ddb6616776c53dfc06bb3f8396df8721c8b699294271fd0c3d110dd278d9c723
 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -1776,8 +1777,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P b8d35c4a7c99ce3753761e5b81269d52c3c910c603fa70b72549883ba68bc485 76094345821246c887a31a234b339d03a78eddbb9fab33b06c3c20797c038349
-R 926c099479c1d3f76ac3c956522bfabe
-T +closed 76094345821246c887a31a234b339d03a78eddbb9fab33b06c3c20797c038349
-U drh
-Z 58f19e654e2db3cbbf8bd66e3b3e9711
+P 11d98414eac467affb0b3cf0c7e5cc3d43184fc2b6e7e898bb5277b51ea9e1fa
+R 147d5d75296309ad726c7c64dc2b1650
+T *branch * changesetfuzz
+T *sym-changesetfuzz *
+T -sym-trunk *
+U dan
+Z e5d17ad373a9d021c09070dfd9d8422e
index 768477b0a3d371c73b78fb03eb79d1f735a83a3a..d808b7ec90590e5f605fceb96bcfc8344b90d45b 100644 (file)
@@ -1 +1 @@
-11d98414eac467affb0b3cf0c7e5cc3d43184fc2b6e7e898bb5277b51ea9e1fa
\ No newline at end of file
+81ac8745faac0bda8d68ac113f1938f0e25208642e8ceb2af452680086454cb5
\ No newline at end of file