SQLite

Check-in [d655bfabd1]
Login

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

Overview
Comment:Fix a race condition in os_unix.c that might allow a client to use a *-shm file corrupted by a power failure if another client fails between locking the *-shm file and truncating it to zero bytes.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | readonly-wal-recovery
Files: files | file ages | folders
SHA3-256: d655bfabd110999b6808073c334869c5b6a8334df56811df883e47e56d3f1cbb
User & Date: dan 2017-11-01 06:59:19.745
Context
2017-11-01
07:06
Merge latest trunk changes into this branch. (check-in: 985bfc9929 user: dan tags: readonly-wal-recovery)
06:59
Fix a race condition in os_unix.c that might allow a client to use a *-shm file corrupted by a power failure if another client fails between locking the *-shm file and truncating it to zero bytes. (check-in: d655bfabd1 user: dan tags: readonly-wal-recovery)
2017-10-26
17:34
Fix an error in the previous commit on this branch. (check-in: f71dfee06c user: dan tags: readonly-wal-recovery)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/os_unix.c.
4368
4369
4370
4371
4372
4373
4374

4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391

4392
4393
4394


4395

4396










4397
4398
4399
4400
4401
4402
4403
4404
4405
4406

4407
4408
4409
4410
4411
4412
4413




4414

4415
4416
4417
4418
4419
4420
4421
      if( pShmNode->mutex==0 ){
        rc = SQLITE_NOMEM_BKPT;
        goto shm_open_err;
      }
    }

    if( pInode->bProcessLock==0 ){

      int openFlags = O_RDWR | O_CREAT;
      if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
        openFlags = O_RDONLY;
        pShmNode->isReadonly = 1;
      }
      pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777));
      if( pShmNode->h<0 ){
        rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
        goto shm_open_err;
      }

      /* If this process is running as root, make sure that the SHM file
      ** is owned by the same user that owns the original database.  Otherwise,
      ** the original owner will not be able to connect.
      */
      robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
  

      /* Check to see if another process is holding the dead-man switch.
      ** For a readonly_shm client, if no other process holds the DMS lock,
      ** the file cannot be opened and SQLITE_CANTOPEN_DIRTYWAL is returned.


      ** Or, for a read-write connection, if no other process holds a

      ** DMS lock the file is truncated to zero bytes in size.  */










      rc = SQLITE_OK;
      if( pShmNode->isReadonly ){
        struct flock lock;
        lock.l_whence = SEEK_SET;
        lock.l_start = UNIX_SHM_DMS;
        lock.l_len = 1;
        lock.l_type = F_WRLCK;
        if( osFcntl(pShmNode->h, F_GETLK, &lock)!=0 ) {
          rc = SQLITE_IOERR_LOCK;
        }else if( lock.l_type==F_UNLCK ){

          rc = SQLITE_CANTOPEN_DIRTYWAL;
        }
      }else if( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
        if( robust_ftruncate(pShmNode->h, 0) ){
          rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename);
        }
      }




      if( rc==SQLITE_OK ){

        rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
      }
      if( rc ) goto shm_open_err;
    }
  }

  /* Make the new connection a child of the unixShmNode */







>

















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

<
<
|
|
|
|
|
|
|
>

|
|
|
|
|
|
>
>
>
>

>







4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395

4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411


4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
      if( pShmNode->mutex==0 ){
        rc = SQLITE_NOMEM_BKPT;
        goto shm_open_err;
      }
    }

    if( pInode->bProcessLock==0 ){
      struct flock lock;
      int openFlags = O_RDWR | O_CREAT;
      if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
        openFlags = O_RDONLY;
        pShmNode->isReadonly = 1;
      }
      pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777));
      if( pShmNode->h<0 ){
        rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
        goto shm_open_err;
      }

      /* If this process is running as root, make sure that the SHM file
      ** is owned by the same user that owns the original database.  Otherwise,
      ** the original owner will not be able to connect.
      */
      robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
  
      /* Use F_GETLK to determine the locks other processes are holding
      ** on the DMS byte. If it indicates that another process is holding
      ** a SHARED lock, then this process may also take a SHARED lock

      ** and proceed with opening the *-shm file. 
      **
      ** Or, if no other process is holding any lock, then this process
      ** is the first to open it. In this case take an EXCLUSIVE lock on the
      ** DMS byte and truncate the *-shm file to zero bytes in size. Then
      ** downgrade to a SHARED lock on the DMS byte.
      **
      ** If another process is holding an EXCLUSIVE lock on the DMS byte,
      ** return SQLITE_BUSY to the caller (it will try again). An earlier
      ** version of this code attempted the SHARED lock at this point. But
      ** this introduced a subtle race condition: if the process holding
      ** EXCLUSIVE failed just before truncating the *-shm file, then this
      ** process might open and use the *-shm file without truncating it.
      ** And if the *-shm file has been corrupted by a power failure or
      ** system crash, the database itself may also become corrupt.  */
      rc = SQLITE_OK;


      lock.l_whence = SEEK_SET;
      lock.l_start = UNIX_SHM_DMS;
      lock.l_len = 1;
      lock.l_type = F_WRLCK;
      if( osFcntl(pShmNode->h, F_GETLK, &lock)!=0 ) {
        rc = SQLITE_IOERR_LOCK;
      }else if( lock.l_type==F_UNLCK ){
        if( pShmNode->isReadonly ){
          rc = SQLITE_CANTOPEN_DIRTYWAL;
        }else{
          rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
          if( rc==SQLITE_OK && robust_ftruncate(pShmNode->h, 0) ){
            rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename);
          }
        }
      }else if( lock.l_type==F_WRLCK ){
        rc = SQLITE_BUSY;
      }

      if( rc==SQLITE_OK ){
        assert( lock.l_type==F_UNLCK || lock.l_type==F_RDLCK );
        rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
      }
      if( rc ) goto shm_open_err;
    }
  }

  /* Make the new connection a child of the unixShmNode */