/ Check-in [2447e0fd]
Login

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

Overview
Comment:Fix a heap-corruption causing race condition in os_unix.c that could occur when one thread wal opening a database file while another is unlocking the same file. Edit: Let's go in a slightly different direction with this fix.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | unix-lock-fix-attempt
Files: files | file ages | folders
SHA3-256: 2447e0fd9830b4de9feeb178a0bff67a9065e3072120d4453bcdd3950d681adb
User & Date: dan 2018-08-13 12:58:48
Original Comment: Fix a heap-corruption causing race condition in os_unix.c that could occur when one thread wal opening a database file while another is unlocking the same file.
Context
2018-08-13
12:58
Fix a heap-corruption causing race condition in os_unix.c that could occur when one thread wal opening a database file while another is unlocking the same file. Edit: Let's go in a slightly different direction with this fix. Closed-Leaf check-in: 2447e0fd user: dan tags: unix-lock-fix-attempt
11:32
Fix an incorrect comment on the unix-nolock VFS object. No functional code changes. check-in: 90f7c193 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/os_unix.c.

   710    710   static void unixLeaveMutex(void){
   711    711     sqlite3_mutex_leave(unixBigLock);
   712    712   }
   713    713   #ifdef SQLITE_DEBUG
   714    714   static int unixMutexHeld(void) {
   715    715     return sqlite3_mutex_held(unixBigLock);
   716    716   }
          717  +static int unixMutexNotheld(void) {
          718  +  return sqlite3_mutex_notheld(unixBigLock);
          719  +}
   717    720   #endif
   718    721   
   719    722   
   720    723   #ifdef SQLITE_HAVE_OS_TRACE
   721    724   /*
   722    725   ** Helper function for printing out trace information from debugging
   723    726   ** binaries. This returns the string representation of the supplied
................................................................................
  1245   1248   static void storeLastErrno(unixFile *pFile, int error){
  1246   1249     pFile->lastErrno = error;
  1247   1250   }
  1248   1251   
  1249   1252   /*
  1250   1253   ** Close all file descriptors accumuated in the unixInodeInfo->pUnused list.
  1251   1254   */ 
  1252         -static void closePendingFds(unixFile *pFile){
         1255  +static void closePendingFdsUnsafe(unixFile *pFile){
  1253   1256     unixInodeInfo *pInode = pFile->pInode;
  1254   1257     UnixUnusedFd *p;
  1255   1258     UnixUnusedFd *pNext;
  1256   1259     for(p=pInode->pUnused; p; p=pNext){
  1257   1260       pNext = p->pNext;
  1258   1261       robust_close(pFile, p->fd, __LINE__);
  1259   1262       sqlite3_free(p);
  1260   1263       nUnusedFd--;
  1261   1264     }
  1262   1265     pInode->pUnused = 0;
  1263   1266   }
         1267  +
         1268  +static void closePendingFds(unixFile *pFile){
         1269  +  unixEnterMutex();
         1270  +  closePendingFdsUnsafe(pFile);
         1271  +  unixLeaveMutex();
         1272  +}
  1264   1273   
  1265   1274   /*
  1266   1275   ** Release a unixInodeInfo structure previously allocated by findInodeInfo().
  1267   1276   **
  1268   1277   ** The mutex entered using the unixEnterMutex() function must be held
  1269   1278   ** when this function is called.
  1270   1279   */
................................................................................
  1271   1280   static void releaseInodeInfo(unixFile *pFile){
  1272   1281     unixInodeInfo *pInode = pFile->pInode;
  1273   1282     assert( unixMutexHeld() );
  1274   1283     if( ALWAYS(pInode) ){
  1275   1284       pInode->nRef--;
  1276   1285       if( pInode->nRef==0 ){
  1277   1286         assert( pInode->pShmNode==0 );
  1278         -      closePendingFds(pFile);
         1287  +      closePendingFdsUnsafe(pFile);
  1279   1288         if( pInode->pPrev ){
  1280   1289           assert( pInode->pPrev->pNext==pInode );
  1281   1290           pInode->pPrev->pNext = pInode->pNext;
  1282   1291         }else{
  1283   1292           assert( inodeList==pInode );
  1284   1293           inodeList = pInode->pNext;
  1285   1294         }
................................................................................
  1454   1463     int reserved = 0;
  1455   1464     unixFile *pFile = (unixFile*)id;
  1456   1465   
  1457   1466     SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
  1458   1467   
  1459   1468     assert( pFile );
  1460   1469     assert( pFile->eFileLock<=SHARED_LOCK );
         1470  +  assert( unixMutexNotheld() );
  1461   1471     sqlite3_mutex_enter(pFile->pInode->pLockMutex);
  1462   1472   
  1463   1473     /* Check if a thread in this process holds such a lock */
  1464   1474     if( pFile->pInode->eFileLock>SHARED_LOCK ){
  1465   1475       reserved = 1;
  1466   1476     }
  1467   1477   
................................................................................
  1666   1676     assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK );
  1667   1677     assert( eFileLock!=PENDING_LOCK );
  1668   1678     assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK );
  1669   1679   
  1670   1680     /* This mutex is needed because pFile->pInode is shared across threads
  1671   1681     */
  1672   1682     pInode = pFile->pInode;
         1683  +  assert( unixMutexNotheld() );
  1673   1684     sqlite3_mutex_enter(pInode->pLockMutex);
  1674   1685   
  1675   1686     /* If some thread using this PID has a lock via a different unixFile*
  1676   1687     ** handle that precludes the requested lock, return BUSY.
  1677   1688     */
  1678   1689     if( (pFile->eFileLock!=pInode->eFileLock && 
  1679   1690             (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK))
................................................................................
  1858   1869         osGetpid(0)));
  1859   1870   
  1860   1871     assert( eFileLock<=SHARED_LOCK );
  1861   1872     if( pFile->eFileLock<=eFileLock ){
  1862   1873       return SQLITE_OK;
  1863   1874     }
  1864   1875     pInode = pFile->pInode;
         1876  +  assert( unixMutexNotheld() );
  1865   1877     sqlite3_mutex_enter(pInode->pLockMutex);
  1866   1878     assert( pInode->nShared!=0 );
  1867   1879     if( pFile->eFileLock>SHARED_LOCK ){
  1868   1880       assert( pInode->eFileLock==pFile->eFileLock );
  1869   1881   
  1870   1882   #ifdef SQLITE_DEBUG
  1871   1883       /* When reducing a lock such that other processes can start
................................................................................
  2789   2801     
  2790   2802     assert( pFile );
  2791   2803     context = (afpLockingContext *) pFile->lockingContext;
  2792   2804     if( context->reserved ){
  2793   2805       *pResOut = 1;
  2794   2806       return SQLITE_OK;
  2795   2807     }
         2808  +  assert( unixMutexNotheld() );
  2796   2809     sqlite3_mutex_enter(pFile->pInode->pLockMutex);
  2797   2810     /* Check if a thread in this process holds such a lock */
  2798   2811     if( pFile->pInode->eFileLock>SHARED_LOCK ){
  2799   2812       reserved = 1;
  2800   2813     }
  2801   2814     
  2802   2815     /* Otherwise see if some other process holds it.
................................................................................
  2877   2890     assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK );
  2878   2891     assert( eFileLock!=PENDING_LOCK );
  2879   2892     assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK );
  2880   2893     
  2881   2894     /* This mutex is needed because pFile->pInode is shared across threads
  2882   2895     */
  2883   2896     pInode = pFile->pInode;
         2897  +  assert( unixMutexNotheld() );
  2884   2898     sqlite3_mutex_enter(pInode->pLockMutex);
  2885   2899   
  2886   2900     /* If some thread using this PID has a lock via a different unixFile*
  2887   2901     ** handle that precludes the requested lock, return BUSY.
  2888   2902     */
  2889   2903     if( (pFile->eFileLock!=pInode->eFileLock && 
  2890   2904          (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK))
................................................................................
  3046   3060              osGetpid(0)));
  3047   3061   
  3048   3062     assert( eFileLock<=SHARED_LOCK );
  3049   3063     if( pFile->eFileLock<=eFileLock ){
  3050   3064       return SQLITE_OK;
  3051   3065     }
  3052   3066     pInode = pFile->pInode;
         3067  +  assert( unixMutexNotheld() );
  3053   3068     sqlite3_mutex_enter(pInode->pLockMutex);
  3054   3069     assert( pInode->nShared!=0 );
  3055   3070     if( pFile->eFileLock>SHARED_LOCK ){
  3056   3071       assert( pInode->eFileLock==pFile->eFileLock );
  3057   3072       SimulateIOErrorBenign(1);
  3058   3073       SimulateIOError( h=(-1) )
  3059   3074       SimulateIOErrorBenign(0);