/ Check-in [d655bfab]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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 | SQL 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
Context
2017-11-01
07:06
Merge latest trunk changes into this branch. check-in: 985bfc99 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: d655bfab user: dan tags: readonly-wal-recovery
2017-10-26
17:34
Fix an error in the previous commit on this branch. check-in: f71dfee0 user: dan tags: readonly-wal-recovery
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/os_unix.c.

  4368   4368         if( pShmNode->mutex==0 ){
  4369   4369           rc = SQLITE_NOMEM_BKPT;
  4370   4370           goto shm_open_err;
  4371   4371         }
  4372   4372       }
  4373   4373   
  4374   4374       if( pInode->bProcessLock==0 ){
         4375  +      struct flock lock;
  4375   4376         int openFlags = O_RDWR | O_CREAT;
  4376   4377         if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
  4377   4378           openFlags = O_RDONLY;
  4378   4379           pShmNode->isReadonly = 1;
  4379   4380         }
  4380   4381         pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777));
  4381   4382         if( pShmNode->h<0 ){
................................................................................
  4385   4386   
  4386   4387         /* If this process is running as root, make sure that the SHM file
  4387   4388         ** is owned by the same user that owns the original database.  Otherwise,
  4388   4389         ** the original owner will not be able to connect.
  4389   4390         */
  4390   4391         robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
  4391   4392     
  4392         -      /* Check to see if another process is holding the dead-man switch.
  4393         -      ** For a readonly_shm client, if no other process holds the DMS lock,
  4394         -      ** the file cannot be opened and SQLITE_CANTOPEN_DIRTYWAL is returned.
  4395         -      ** Or, for a read-write connection, if no other process holds a
  4396         -      ** DMS lock the file is truncated to zero bytes in size.  */
         4393  +      /* Use F_GETLK to determine the locks other processes are holding
         4394  +      ** on the DMS byte. If it indicates that another process is holding
         4395  +      ** a SHARED lock, then this process may also take a SHARED lock
         4396  +      ** and proceed with opening the *-shm file. 
         4397  +      **
         4398  +      ** Or, if no other process is holding any lock, then this process
         4399  +      ** is the first to open it. In this case take an EXCLUSIVE lock on the
         4400  +      ** DMS byte and truncate the *-shm file to zero bytes in size. Then
         4401  +      ** downgrade to a SHARED lock on the DMS byte.
         4402  +      **
         4403  +      ** If another process is holding an EXCLUSIVE lock on the DMS byte,
         4404  +      ** return SQLITE_BUSY to the caller (it will try again). An earlier
         4405  +      ** version of this code attempted the SHARED lock at this point. But
         4406  +      ** this introduced a subtle race condition: if the process holding
         4407  +      ** EXCLUSIVE failed just before truncating the *-shm file, then this
         4408  +      ** process might open and use the *-shm file without truncating it.
         4409  +      ** And if the *-shm file has been corrupted by a power failure or
         4410  +      ** system crash, the database itself may also become corrupt.  */
  4397   4411         rc = SQLITE_OK;
  4398         -      if( pShmNode->isReadonly ){
  4399         -        struct flock lock;
  4400         -        lock.l_whence = SEEK_SET;
  4401         -        lock.l_start = UNIX_SHM_DMS;
  4402         -        lock.l_len = 1;
  4403         -        lock.l_type = F_WRLCK;
  4404         -        if( osFcntl(pShmNode->h, F_GETLK, &lock)!=0 ) {
  4405         -          rc = SQLITE_IOERR_LOCK;
  4406         -        }else if( lock.l_type==F_UNLCK ){
         4412  +      lock.l_whence = SEEK_SET;
         4413  +      lock.l_start = UNIX_SHM_DMS;
         4414  +      lock.l_len = 1;
         4415  +      lock.l_type = F_WRLCK;
         4416  +      if( osFcntl(pShmNode->h, F_GETLK, &lock)!=0 ) {
         4417  +        rc = SQLITE_IOERR_LOCK;
         4418  +      }else if( lock.l_type==F_UNLCK ){
         4419  +        if( pShmNode->isReadonly ){
  4407   4420             rc = SQLITE_CANTOPEN_DIRTYWAL;
         4421  +        }else{
         4422  +          rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
         4423  +          if( rc==SQLITE_OK && robust_ftruncate(pShmNode->h, 0) ){
         4424  +            rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename);
         4425  +          }
  4408   4426           }
  4409         -      }else if( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
  4410         -        if( robust_ftruncate(pShmNode->h, 0) ){
  4411         -          rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename);
  4412         -        }
         4427  +      }else if( lock.l_type==F_WRLCK ){
         4428  +        rc = SQLITE_BUSY;
  4413   4429         }
         4430  +
  4414   4431         if( rc==SQLITE_OK ){
         4432  +        assert( lock.l_type==F_UNLCK || lock.l_type==F_RDLCK );
  4415   4433           rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
  4416   4434         }
  4417   4435         if( rc ) goto shm_open_err;
  4418   4436       }
  4419   4437     }
  4420   4438   
  4421   4439     /* Make the new connection a child of the unixShmNode */