/ Check-in [525f75fa]
Login

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

Overview
Comment:Fix a problem causing sqlite3_snapshot_recover() to return SQLITE_IOERR_SHORT_READ.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | serializable-snapshot
Files: files | file ages | folders
SHA1: 525f75fa9fd4a95acc3fb3b0a01dabe2be39b383
User & Date: dan 2016-11-19 16:35:53
Context
2016-11-19
17:20
Add another fault-injection test for sqlite3_snapshot_recover(). check-in: 7e040406 user: dan tags: serializable-snapshot
16:35
Fix a problem causing sqlite3_snapshot_recover() to return SQLITE_IOERR_SHORT_READ. check-in: 525f75fa user: dan tags: serializable-snapshot
14:53
Fix a bug in sqlite3_snapshot_recover() that could cause subsequent read transactions to use out-of-data cache entries. check-in: 9abeb798 user: dan tags: serializable-snapshot
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/wal.c.

2388
2389
2390
2391
2392
2393
2394




2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410

2411
2412
2413


2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425

2426
2427
2428
2429
2430
2431

2432
2433
2434
2435
2436
2437
2438

  rc = sqlite3WalBeginReadTransaction(pWal, &dummy);
  if( rc==SQLITE_OK ){
    rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
    if( rc==SQLITE_OK ){
      volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
      int szPage = (int)pWal->szPage;




      void *pBuf1 = sqlite3_malloc(szPage);
      void *pBuf2 = sqlite3_malloc(szPage);

      if( pBuf1==0 || pBuf2==0 ){
        rc = SQLITE_NOMEM;
      }else{
        u32 i = pInfo->nBackfillAttempted;
        for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){
          volatile ht_slot *dummy;
          volatile u32 *aPgno;      /* Array of page numbers */
          u32 iZero;                /* Frame corresponding to aPgno[0] */
          u32 pgno;                 /* Page number in db file */
          i64 iDbOff;               /* Offset of db file entry */
          i64 iWalOff;              /* Offset of wal file entry */
          rc = walHashGet(pWal, walFramePage(i), &dummy, &aPgno, &iZero);


          if( rc==SQLITE_OK ){
            pgno = aPgno[i-iZero];
            iDbOff = (i64)(pgno-1) * szPage;


            iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE;
            rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff);
          }

          if( rc==SQLITE_OK ){
            rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff);
          }

          if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){
            break;
          }


          pInfo->nBackfillAttempted = i-1;
        }
      }

      sqlite3_free(pBuf1);
      sqlite3_free(pBuf2);

      walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
    }

    /* End the read transaction opened above. Also zero the cache of the
    ** wal-index header to force the pager-cache to be flushed when the next
    ** read transaction is open, as it may not match the current contents of
    ** pWal->hdr. */







>
>
>
>
|
|
<
|
|
|
|
|
|
|
|
|
|
|
<

>
|


>
>
|
|
|
<
|
|
|

|
|
|
|
>
|
|
|

|
|
>







2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400

2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411

2412
2413
2414
2415
2416
2417
2418
2419
2420
2421

2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444

  rc = sqlite3WalBeginReadTransaction(pWal, &dummy);
  if( rc==SQLITE_OK ){
    rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
    if( rc==SQLITE_OK ){
      volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
      int szPage = (int)pWal->szPage;
      i64 szDb;                   /* Size of db file in bytes */

      rc = sqlite3OsFileSize(pWal->pDbFd, &szDb);
      if( rc==SQLITE_OK ){
        void *pBuf1 = sqlite3_malloc(szPage);
        void *pBuf2 = sqlite3_malloc(szPage);

        if( pBuf1==0 || pBuf2==0 ){
          rc = SQLITE_NOMEM;
        }else{
          u32 i = pInfo->nBackfillAttempted;
          for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){
            volatile ht_slot *dummy;
            volatile u32 *aPgno;      /* Array of page numbers */
            u32 iZero;                /* Frame corresponding to aPgno[0] */
            u32 pgno;                 /* Page number in db file */
            i64 iDbOff;               /* Offset of db file entry */
            i64 iWalOff;              /* Offset of wal file entry */


            rc = walHashGet(pWal, walFramePage(i), &dummy, &aPgno, &iZero);
            if( rc!=SQLITE_OK ) break;
            pgno = aPgno[i-iZero];
            iDbOff = (i64)(pgno-1) * szPage;

            if( iDbOff+szPage<=szDb ){
              iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE;
              rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff);


              if( rc==SQLITE_OK ){
                rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff);
              }

              if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){
                break;
              }
            }

            pInfo->nBackfillAttempted = i-1;
          }
        }

        sqlite3_free(pBuf1);
        sqlite3_free(pBuf2);
      }
      walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
    }

    /* End the read transaction opened above. Also zero the cache of the
    ** wal-index header to force the pager-cache to be flushed when the next
    ** read transaction is open, as it may not match the current contents of
    ** pWal->hdr. */

Changes to test/snapshot_fault.test.

12
13
14
15
16
17
18


19
20
21
22
23
24
25
...
155
156
157
158
159
160
161
162
163





























164
# of this file is the sqlite3_snapshot_xxx() APIs.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !snapshot {finish_test; return}
set testprefix snapshot_fault



#-------------------------------------------------------------------------
# Check that an sqlite3_snapshot_open() client cannot be tricked into
# reading a corrupt snapshot even if a second client fails while 
# checkpointing the db.
#
do_faultsim_test 1.0 -prep {
................................................................................
    }]
    if {$res != "1 2 3 ok"} { error "res is $res" }
  }

  sqlite3_snapshot_free $::snapshot
}
































finish_test







>
>







 







|

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

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
...
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# of this file is the sqlite3_snapshot_xxx() APIs.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !snapshot {finish_test; return}
set testprefix snapshot_fault

if 1 {

#-------------------------------------------------------------------------
# Check that an sqlite3_snapshot_open() client cannot be tricked into
# reading a corrupt snapshot even if a second client fails while 
# checkpointing the db.
#
do_faultsim_test 1.0 -prep {
................................................................................
    }]
    if {$res != "1 2 3 ok"} { error "res is $res" }
  }

  sqlite3_snapshot_free $::snapshot
}

}

#-------------------------------------------------------------------------
# Test the handling of faults that occur within sqlite3_snapshot_recover().
#
reset_db
do_execsql_test 4.0 {
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(zzz);
  INSERT INTO t1 VALUES('abc');
  INSERT INTO t1 VALUES('def');
} {wal}
faultsim_save_and_close

do_test 4.1 {
  faultsim_restore_and_reopen
  db eval { SELECT * FROM sqlite_master } 
  sqlite3_snapshot_recover db main
} {}
db close

do_faultsim_test 4 -faults oom* -prep {
  faultsim_restore_and_reopen
  db eval { SELECT * FROM sqlite_master } 
} -body {
  sqlite3_snapshot_recover db main
} -test {
  faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 SQLITE_IOERR_NOMEM}
}


finish_test