/ Check-in [5c7f0240]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix minor issues in the changesetfuzz program.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | changesetfuzz
Files: files | file ages | folders
SHA3-256:5c7f024073bc93089f038b5cf122a7a9d5b933f7c1b357f6d20ae925739ffc38
User & Date: dan 2018-11-07 20:07:28
Context
2018-11-07
20:13
Merge latest trunk changes into this branch. check-in: 53cd91d0 user: dan tags: changesetfuzz
20:07
Fix minor issues in the changesetfuzz program. check-in: 5c7f0240 user: dan tags: changesetfuzz
17:52
Update the "changesetfuzz" program to work with patchsets as well as changesets. check-in: 75b00fbe user: dan tags: changesetfuzz
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to ext/session/changesetfuzz.c.

21
22
23
24
25
26
27




28
29
30
31
32
33
34
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
...
426
427
428
429
430
431
432





433
434
435
436


437
438
439
440
441
442
443
444
...
468
469
470
471
472
473
474







475
476
477
478
479
480
481
...
496
497
498
499
500
501
502













503
504
505
506
507
508
509
510
511
512
513
514
...
530
531
532
533
534
535
536








537
538
539
540
541
542
543
...
563
564
565
566
567
568
569






570
571
572
573
574
575
576
...
604
605
606
607
608
609
610












611
612
613
614
615
616
617
...
672
673
674
675
676
677
678




679
680
681
682
683
684
685
686
...
703
704
705
706
707
708
709










710
711
712
713
714
715
716
...
833
834
835
836
837
838
839




840
841
842
843
844
845
846
....
1018
1019
1020
1021
1022
1023
1024







1025
1026
1027
1028
1029
1030
1031
**
**   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 
................................................................................
**      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.
**
** PATCHSETS
**
** As well as changesets, this program can also dump and fuzz patchsets.
*/

#include "sqlite3.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
................................................................................
  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(
  FuzzChangeset *pParse,
  u8 **ppHdr, 
  u8 *pEnd, 


  FuzzChangesetGroup **ppGrp
){
  int rc = SQLITE_OK;
  FuzzChangesetGroup *pGrp;
  u8 cHdr = (pParse->bPatchset ? 'P' : 'T');

  assert( pEnd>(*ppHdr) );
  pGrp = (FuzzChangesetGroup*)fuzzMalloc(sizeof(FuzzChangesetGroup));
................................................................................
    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;
................................................................................

    default:
      return fuzzCorrupt();
  }
  return SQLITE_OK;
}














static int fuzzParseRecord(
  u8 **ppRec,                     /* IN/OUT: Iterator */
  u8 *pEnd,                       /* One byte after end of input data */
  FuzzChangeset *pParse,
  int bPkOnly
){
  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 && p<pEnd; i++){
................................................................................
    rc = fuzzCorrupt();
  }

  *ppRec = p;
  return rc;
}









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;
................................................................................
  }
  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;
................................................................................
      rc = fuzzParseChanges(&p, pEnd, pParse);
    }
  }

  return rc;
}













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++){
................................................................................
  }
  printf(")");

  *ppRec = p;
  return rc;
}





static int 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]);
................................................................................
      fuzzPrintRecord(pGrp, &p, 0);
    }
    fuzzPrintRecord(pGrp, &p, eType==SQLITE_DELETE && pParse->bPatchset);
    printf("\n");
  }
}











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
................................................................................
      }
    }
  }

  return SQLITE_OK;
}





static int fuzzCopyChange(
  FuzzChangeset *pParse,
  int iGrp,
  FuzzChange *pFuzz,
  u8 **pp, u8 **ppOut             /* IN/OUT: Input and output pointers */
){
  int bPS = pParse->bPatchset;
................................................................................

  *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;







>
>
>
>







 







<
<
<
<







 







>
>
>
>
>

|
<
<
>
>
|







 







>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>



|
|







 







>
>
>
>
>
>
>
>







 







>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
|







 







>
>
>
>
>
>
>
>
>
>







 







>
>
>
>







 







>
>
>
>
>
>
>







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
90
91
92
93
94
95
96




97
98
99
100
101
102
103
...
426
427
428
429
430
431
432
433
434
435
436
437
438
439


440
441
442
443
444
445
446
447
448
449
...
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
...
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
...
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
...
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
...
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
...
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
...
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
...
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
....
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
**
**   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 
................................................................................
**      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>
................................................................................
  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));
................................................................................
    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;
................................................................................

    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 && p<pEnd; i++){
................................................................................
    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;
................................................................................
  }
  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;
................................................................................
      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++){
................................................................................
  }
  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]);
................................................................................
      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
................................................................................
      }
    }
  }

  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;
................................................................................

  *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;