Fix a race condition in sqlite3_initialize(). (CVS 5310)

FossilOrigin-Name: 70b2ed2afcf1757d1c58f3a83dad4a5fb226ae63
diff --git a/src/main.c b/src/main.c
index 520c16a..b2cfa11 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.462 2008/06/25 17:19:01 danielk1977 Exp $
+** $Id: main.c,v 1.463 2008/06/26 08:29:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -66,26 +66,56 @@
 ** or for the first call after a call to sqlite3_shutdown.
 */
 int sqlite3_initialize(void){
+  static int inProgress = 0;
   int rc;
+
+  /* If SQLite is already initialized, this call is a no-op. */
   if( sqlite3Config.isInit ) return SQLITE_OK;
+
+  /* Make sure the mutex system is initialized. */
   rc = sqlite3MutexInit();
+
   if( rc==SQLITE_OK ){
-#ifndef SQLITE_MUTEX_NOOP
-    sqlite3_mutex *pMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
-#endif
-    sqlite3_mutex_enter(pMutex);
-    if( sqlite3Config.isInit==0 ){
-      sqlite3Config.isInit = 1;
-      sqlite3StatusReset();
-      if( rc==SQLITE_OK ) rc = sqlite3MallocInit();
-      if( rc==SQLITE_OK ) rc = sqlite3_os_init();
-      if( rc!=SQLITE_OK ){
-        sqlite3Config.isInit = 0;
-      }else{
-        sqlite3Config.isInit = 2;
+
+    /* Initialize the malloc() system and the recursive pInitMutex mutex.
+    ** This operation is protected by the STATIC_MASTER mutex.
+    */
+    sqlite3_mutex *pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+    sqlite3_mutex_enter(pMaster);
+    if( !sqlite3Config.isMallocInit ){
+      rc = sqlite3MallocInit();
+    }
+    if( rc==SQLITE_OK ){
+      sqlite3Config.isMallocInit = 1;
+      if( !sqlite3Config.pInitMutex ){
+        sqlite3Config.pInitMutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
+        if( sqlite3Config.bCoreMutex && !sqlite3Config.pInitMutex ){
+          rc = SQLITE_NOMEM;
+        }
       }
     }
-    sqlite3_mutex_leave(pMutex);
+    sqlite3_mutex_leave(pMaster);
+    if( rc!=SQLITE_OK ){
+      return rc;
+    }
+
+    /* Enter the recursive pInitMutex mutex. After doing so, if the
+    ** sqlite3Config.isInit flag is true, then some other thread has
+    ** finished doing the initialization. If the inProgress flag is
+    ** true, then this function is being called recursively from within
+    ** the sqlite3_os_init() call below. In either case, exit early.
+    */
+    sqlite3_mutex_enter(sqlite3Config.pInitMutex);
+    if( sqlite3Config.isInit || inProgress ){
+      sqlite3_mutex_leave(sqlite3Config.pInitMutex);
+      return SQLITE_OK;
+    }
+    sqlite3StatusReset();
+    inProgress = 1;
+    rc = sqlite3_os_init();
+    inProgress = 0;
+    sqlite3Config.isInit = (rc==SQLITE_OK ? 1 : 0);
+    sqlite3_mutex_leave(sqlite3Config.pInitMutex);
   }
   return rc;
 }
@@ -97,6 +127,9 @@
 ** routine is not threadsafe.  Not by a long shot.
 */
 int sqlite3_shutdown(void){
+  sqlite3_mutex_free(sqlite3Config.pInitMutex);
+  sqlite3Config.pInitMutex = 0;
+  sqlite3Config.isMallocInit = 0;
   sqlite3_os_end();
   sqlite3MallocEnd();
   sqlite3MutexEnd();
@@ -119,7 +152,7 @@
 
   /* sqlite3_config() shall return SQLITE_MISUSE if it is invoked while
   ** the SQLite library is in use. */
-  if( sqlite3Config.isInit==2 ) return SQLITE_MISUSE;
+  if( sqlite3Config.isInit ) return SQLITE_MISUSE;
 
   va_start(ap, op);
   switch( op ){
diff --git a/src/os.c b/src/os.c
index a746cd7..3a58e77 100644
--- a/src/os.c
+++ b/src/os.c
@@ -13,7 +13,7 @@
 ** This file contains OS interface code that is common to all
 ** architectures.
 **
-** $Id: os.c,v 1.116 2008/06/25 17:19:01 danielk1977 Exp $
+** $Id: os.c,v 1.117 2008/06/26 08:29:34 danielk1977 Exp $
 */
 #define _SQLITE_OS_C_ 1
 #include "sqliteInt.h"
@@ -243,18 +243,12 @@
 ** true.
 */
 int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){
-#ifndef SQLITE_MUTEX_NOOP
   sqlite3_mutex *mutex = 0;
-#endif
 #ifndef SQLITE_OMIT_AUTOINIT
   int rc = sqlite3_initialize();
   if( rc ) return rc;
 #endif
-#ifndef SQLITE_MUTEX_NOOP
-  if( sqlite3Config.isInit!=1 ){
-    mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
-  }
-#endif
+  mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
   sqlite3_mutex_enter(mutex);
   vfsUnlink(pVfs);
   if( makeDflt || vfsList==0 ){
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 65c374d..942396a 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.729 2008/06/25 17:19:01 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.730 2008/06/26 08:29:34 danielk1977 Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -1734,12 +1734,6 @@
 ** Structure containing global configuration data for the SQLite library.
 **
 ** This structure also contains some state information.
-**
-** The Sqlite3Config.isInit variable indicates whether or not
-** sqlite3_initialize() has already been called or not. Initially, isInit
-** is 0. While sqlite3_initialize() is running, it is set to 1. After
-** sqlite3_initialize has successfully run, the Sqlite3Config.isInit variable
-** is set to 2. Calling sqlite3_shutdown() resets the value to 0.
 */
 struct Sqlite3Config {
   int bMemstat;                     /* True to enable memory status */
@@ -1756,7 +1750,9 @@
   void *pPage;                      /* Page cache memory */
   int szPage;                       /* Size of each page in pPage[] */
   int nPage;                        /* Number of pages in pPage[] */
-  int isInit;                       /* Initialization state */
+  int isInit;                       /* True after initialization has finished */
+  int isMallocInit;                 /* True after malloc is initialized */
+  sqlite3_mutex *pInitMutex;        /* Mutex used by sqlite3_initialize() */
 };
 
 /*