]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix a race condition in os_unix.c that might allow a client to use a *-shm
authordan <dan@noemail.net>
Wed, 1 Nov 2017 06:59:19 +0000 (06:59 +0000)
committerdan <dan@noemail.net>
Wed, 1 Nov 2017 06:59:19 +0000 (06:59 +0000)
file corrupted by a power failure if another client fails between locking the
*-shm file and truncating it to zero bytes.

FossilOrigin-Name: d655bfabd110999b6808073c334869c5b6a8334df56811df883e47e56d3f1cbb

manifest
manifest.uuid
src/os_unix.c

index ebf38789b553d92ec20170d68fe8d3233375e163..91f071548a4ef1cbc699f999a96ff307cf938db9 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\san\serror\sin\sthe\sprevious\scommit\son\sthis\sbranch.
-D 2017-10-26T17:34:50.823
+C Fix\sa\srace\scondition\sin\sos_unix.c\sthat\smight\sallow\sa\sclient\sto\suse\sa\s*-shm\nfile\scorrupted\sby\sa\spower\sfailure\sif\sanother\sclient\sfails\sbetween\slocking\sthe\n*-shm\sfile\sand\struncating\sit\sto\szero\sbytes.
+D 2017-11-01T06:59:19.745
 F Makefile.in e016061b23e60ac9ec27c65cb577292b6bde0307ca55abd874ab3487b3b1beb2
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 37740aba9c4bb359c627eadccf1cfd7be4f5f847078723777ea7763969e533b1
@@ -447,7 +447,7 @@ F src/os.c 22d31db3ca5a96a408fbf1ceeaaebcaf64c87024d2ff9fe1cf2ddbec3e75c104
 F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432
 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
 F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
-F src/os_unix.c 9bf0c1b7156cbcd2ec32e557cffa319251e1ffb1515d923a2dd2d8eee69b4ee4
+F src/os_unix.c 9137cfdb42e83f4fb599aa2b1c6996f368aacbb410bde53b534e766a61ba65ca
 F src/os_win.c 6892c3ff23b7886577e47f13d827ca220c0831bae3ce00eea8c258352692f8c6
 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
 F src/pager.c 07cf850241667874fcce9d7d924c814305e499b26c804322e2261247b5921903
@@ -1666,7 +1666,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 5492f457dc7cc5c416de4b4e61e84bd2f10b4e6ce54011b7a60feb47f629c923
-R 49c1c99b22f8e84ff20a7afe5d1d547b
+P f71dfee06ce1e0eee760cfca19482bdec7729d6c7d28f10f4cfd21e1f92a04b0
+R 37c523032e6b14675e1621a01cbce4c1
 U dan
-Z 07348874af35473af5ead94c9a86c9df
+Z e56573f2b7ef574fffc6cf13c6dfb22b
index 3da837219d182f10d2ea04ebe598781542aab9de..46c47f2d96febc9322bdf72bf576f000b180e807 100644 (file)
@@ -1 +1 @@
-f71dfee06ce1e0eee760cfca19482bdec7729d6c7d28f10f4cfd21e1f92a04b0
\ No newline at end of file
+d655bfabd110999b6808073c334869c5b6a8334df56811df883e47e56d3f1cbb
\ No newline at end of file
index 7720a255ac01a9130a1a4a0c91b63b771c254366..b94d417f4938178fb28ba6e3d853448f3b71793b 100644 (file)
@@ -4372,6 +4372,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
     }
 
     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;
@@ -4389,29 +4390,46 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
       */
       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.  */
+      /* 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;
-      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 ){
+      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( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
-        if( 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;