Bug 730495, guarantee that sqlite3_config is called before any other SQLite function, r=asuth, r=froydnj, r=mak
authorKai Engert <kaie@kuix.de>
Fri, 29 Sep 2017 13:25:06 +0200
changeset 383801 be147f49de8e87a4818c22e47030b2c7a7f27301
parent 383800 ac3ccd00426152daa9f7afbba00cc6abd59a1039
child 383802 64949972673f2d167b7e62a2dbc6ec37cb6751b1
push id32602
push userkwierso@gmail.com
push dateFri, 29 Sep 2017 21:47:40 +0000
treeherdermozilla-central@57f68296c350 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth, froydnj, mak
bugs730495
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 730495, guarantee that sqlite3_config is called before any other SQLite function, r=asuth, r=froydnj, r=mak
security/manager/ssl/nsNSSComponent.cpp
storage/TelemetryVFS.cpp
storage/mozStorageConnection.cpp
storage/mozStorageService.cpp
toolkit/xre/AutoSQLiteLifetime.cpp
toolkit/xre/AutoSQLiteLifetime.h
toolkit/xre/Bootstrap.cpp
toolkit/xre/moz.build
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -8,17 +8,16 @@
 
 #include "ExtendedValidation.h"
 #include "NSSCertDBTrustDomain.h"
 #include "PKCS11ModuleDB.h"
 #include "ScopedNSSTypes.h"
 #include "SharedSSLState.h"
 #include "cert.h"
 #include "certdb.h"
-#include "mozStorageCID.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/PublicSSL.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
@@ -2258,24 +2257,16 @@ nsNSSComponent::Init()
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
   MOZ_ASSERT(XRE_IsParentProcess());
   if (!XRE_IsParentProcess()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  // To avoid a sqlite3_config race in NSS init, as a workaround for
-  // bug 730495, we require the storage service to get initialized first.
-  nsCOMPtr<nsISupports> storageService =
-    do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
-  if (!storageService) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Beginning NSS initialization\n"));
 
   nsresult rv = InitializePIPNSSBundle();
   if (NS_FAILED(rv)) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("Unable to create pipnss bundle.\n"));
     return rv;
   }
 
--- a/storage/TelemetryVFS.cpp
+++ b/storage/TelemetryVFS.cpp
@@ -829,16 +829,21 @@ xNextSystemCall(sqlite3_vfs *vfs, const 
   return orig_vfs->xNextSystemCall(orig_vfs, zName);
 }
 
 } // namespace
 
 namespace mozilla {
 namespace storage {
 
+const char *GetVFSName()
+{
+  return "telemetry-vfs";
+}
+
 sqlite3_vfs* ConstructTelemetryVFS()
 {
 #if defined(XP_WIN)
 #define EXPECTED_VFS     "win32"
 #define EXPECTED_VFS_NFS "win32"
 #else
 #define EXPECTED_VFS     "unix"
 #define EXPECTED_VFS_NFS "unix-excl"
@@ -862,17 +867,17 @@ sqlite3_vfs* ConstructTelemetryVFS()
   memset(tvfs, 0, sizeof(::sqlite3_vfs));
   // If the VFS version is higher than the last known one, you should update
   // this VFS adding appropriate methods for any methods added in the version
   // change.
   tvfs->iVersion = vfs->iVersion;
   MOZ_ASSERT(vfs->iVersion <= LAST_KNOWN_VFS_VERSION);
   tvfs->szOsFile = sizeof(telemetry_file) - sizeof(sqlite3_file) + vfs->szOsFile;
   tvfs->mxPathname = vfs->mxPathname;
-  tvfs->zName = "telemetry-vfs";
+  tvfs->zName = GetVFSName();
   tvfs->pAppData = vfs;
   tvfs->xOpen = xOpen;
   tvfs->xDelete = xDelete;
   tvfs->xAccess = xAccess;
   tvfs->xFullPathname = xFullPathname;
   tvfs->xDlOpen = xDlOpen;
   tvfs->xDlError = xDlError;
   tvfs->xDlSym = xDlSym;
--- a/storage/mozStorageConnection.cpp
+++ b/storage/mozStorageConnection.cpp
@@ -68,16 +68,18 @@ mozilla::LazyLogModule gStorageLog("mozS
 #define CHECK_MAINTHREAD_ABUSE() do { /* Nothing */ } while(0)
 #endif
 
 namespace mozilla {
 namespace storage {
 
 using mozilla::dom::quota::QuotaObject;
 
+const char *GetVFSName();
+
 namespace {
 
 int
 nsresultToSQLiteResult(nsresult aXPCOMResultCode)
 {
   if (NS_SUCCEEDED(aXPCOMResultCode)) {
     return SQLITE_OK;
   }
@@ -622,17 +624,17 @@ Connection::getAsyncExecutionTarget()
 nsresult
 Connection::initialize()
 {
   NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
   MOZ_ASSERT(!mIgnoreLockingMode, "Can't ignore locking on an in-memory db.");
   AUTO_PROFILER_LABEL("Connection::initialize", STORAGE);
 
   // in memory database requested, sqlite uses a magic file name
-  int srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, nullptr);
+  int srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, GetVFSName());
   if (srv != SQLITE_OK) {
     mDBConn = nullptr;
     return convertResultCode(srv);
   }
 
   // Do not set mDatabaseFile or mFileURL here since this is a "memory"
   // database.
 
@@ -655,17 +657,17 @@ Connection::initialize(nsIFile *aDatabas
   nsresult rv = aDatabaseFile->GetPath(path);
   NS_ENSURE_SUCCESS(rv, rv);
 
 #ifdef XP_WIN
   static const char* sIgnoreLockingVFS = "win32-none";
 #else
   static const char* sIgnoreLockingVFS = "unix-none";
 #endif
-  const char* vfs = mIgnoreLockingMode ? sIgnoreLockingVFS : nullptr;
+  const char* vfs = mIgnoreLockingMode ? sIgnoreLockingVFS : GetVFSName();
 
   int srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn,
                               mFlags, vfs);
   if (srv != SQLITE_OK) {
     mDBConn = nullptr;
     return convertResultCode(srv);
   }
 
@@ -689,17 +691,17 @@ Connection::initialize(nsIFileURL *aFile
   nsCOMPtr<nsIFile> databaseFile;
   nsresult rv = aFileURL->GetFile(getter_AddRefs(databaseFile));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString spec;
   rv = aFileURL->GetSpec(spec);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, nullptr);
+  int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, GetVFSName());
   if (srv != SQLITE_OK) {
     mDBConn = nullptr;
     return convertResultCode(srv);
   }
 
   // Set both mDatabaseFile and mFileURL here.
   mFileURL = aFileURL;
   mDatabaseFile = databaseFile;
--- a/storage/mozStorageService.cpp
+++ b/storage/mozStorageService.cpp
@@ -19,31 +19,25 @@
 #include "nsIPropertyBag2.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/LateWriteChecks.h"
 #include "mozIStorageCompletionCallback.h"
 #include "mozIStoragePendingStatement.h"
 
 #include "sqlite3.h"
+#include "mozilla/AutoSQLiteLifetime.h"
 
 #ifdef SQLITE_OS_WIN
 // "windows.h" was included and it can #define lots of things we care about...
 #undef CompareString
 #endif
 
 #include "nsIPromptService.h"
 
-#ifdef MOZ_STORAGE_MEMORY
-#  include "mozmemory.h"
-#  ifdef MOZ_DMD
-#    include "DMD.h"
-#  endif
-#endif
-
 ////////////////////////////////////////////////////////////////////////////////
 //// Defines
 
 #define PREF_TS_SYNCHRONOUS "toolkit.storage.synchronous"
 #define PREF_TS_SYNCHRONOUS_DEFAULT 1
 
 #define PREF_TS_PAGESIZE "toolkit.storage.pageSize"
 
@@ -277,22 +271,16 @@ Service::~Service()
 {
   mozilla::UnregisterWeakMemoryReporter(this);
   mozilla::UnregisterStorageSQLiteDistinguishedAmount();
 
   int rc = sqlite3_vfs_unregister(mSqliteVFS);
   if (rc != SQLITE_OK)
     NS_WARNING("Failed to unregister sqlite vfs wrapper.");
 
-  // Shutdown the sqlite3 API.  Warn if shutdown did not turn out okay, but
-  // there is nothing actionable we can do in that case.
-  rc = ::sqlite3_shutdown();
-  if (rc != SQLITE_OK)
-    NS_WARNING("sqlite3 did not shutdown cleanly.");
-
   shutdown(); // To release sXPConnect.
 
   gService = nullptr;
   delete mSqliteVFS;
   mSqliteVFS = nullptr;
 }
 
 void
@@ -395,165 +383,36 @@ Service::minimizeMemory()
 
 void
 Service::shutdown()
 {
   NS_IF_RELEASE(sXPConnect);
 }
 
 sqlite3_vfs *ConstructTelemetryVFS();
-
-#ifdef MOZ_STORAGE_MEMORY
-
-namespace {
-
-// By default, SQLite tracks the size of all its heap blocks by adding an extra
-// 8 bytes at the start of the block to hold the size.  Unfortunately, this
-// causes a lot of 2^N-sized allocations to be rounded up by jemalloc
-// allocator, wasting memory.  For example, a request for 1024 bytes has 8
-// bytes added, becoming a request for 1032 bytes, and jemalloc rounds this up
-// to 2048 bytes, wasting 1012 bytes.  (See bug 676189 for more details.)
-//
-// So we register jemalloc as the malloc implementation, which avoids this
-// 8-byte overhead, and thus a lot of waste.  This requires us to provide a
-// function, sqliteMemRoundup(), which computes the actual size that will be
-// allocated for a given request.  SQLite uses this function before all
-// allocations, and may be able to use any excess bytes caused by the rounding.
-//
-// Note: the wrappers for malloc, realloc and moz_malloc_usable_size are
-// necessary because the sqlite_mem_methods type signatures differ slightly
-// from the standard ones -- they use int instead of size_t.  But we don't need
-// a wrapper for free.
-
-#ifdef MOZ_DMD
-
-// sqlite does its own memory accounting, and we use its numbers in our memory
-// reporters.  But we don't want sqlite's heap blocks to show up in DMD's
-// output as unreported, so we mark them as reported when they're allocated and
-// mark them as unreported when they are freed.
-//
-// In other words, we are marking all sqlite heap blocks as reported even
-// though we're not reporting them ourselves.  Instead we're trusting that
-// sqlite is fully and correctly accounting for all of its heap blocks via its
-// own memory accounting.  Well, we don't have to trust it entirely, because
-// it's easy to keep track (while doing this DMD-specific marking) of exactly
-// how much memory SQLite is using.  And we can compare that against what
-// SQLite reports it is using.
-
-MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(SqliteMallocSizeOfOnAlloc)
-MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(SqliteMallocSizeOfOnFree)
-
-#endif
-
-static void *sqliteMemMalloc(int n)
-{
-  void* p = ::malloc(n);
-#ifdef MOZ_DMD
-  gSqliteMemoryUsed += SqliteMallocSizeOfOnAlloc(p);
-#endif
-  return p;
-}
-
-static void sqliteMemFree(void *p)
-{
-#ifdef MOZ_DMD
-  gSqliteMemoryUsed -= SqliteMallocSizeOfOnFree(p);
-#endif
-  ::free(p);
-}
-
-static void *sqliteMemRealloc(void *p, int n)
-{
-#ifdef MOZ_DMD
-  gSqliteMemoryUsed -= SqliteMallocSizeOfOnFree(p);
-  void *pnew = ::realloc(p, n);
-  if (pnew) {
-    gSqliteMemoryUsed += SqliteMallocSizeOfOnAlloc(pnew);
-  } else {
-    // realloc failed;  undo the SqliteMallocSizeOfOnFree from above
-    gSqliteMemoryUsed += SqliteMallocSizeOfOnAlloc(p);
-  }
-  return pnew;
-#else
-  return ::realloc(p, n);
-#endif
-}
-
-static int sqliteMemSize(void *p)
-{
-  return ::moz_malloc_usable_size(p);
-}
-
-static int sqliteMemRoundup(int n)
-{
-  n = malloc_good_size(n);
-
-  // jemalloc can return blocks of size 2 and 4, but SQLite requires that all
-  // allocations be 8-aligned.  So we round up sub-8 requests to 8.  This
-  // wastes a small amount of memory but is obviously safe.
-  return n <= 8 ? 8 : n;
-}
-
-static int sqliteMemInit(void *p)
-{
-  return 0;
-}
-
-static void sqliteMemShutdown(void *p)
-{
-}
-
-const sqlite3_mem_methods memMethods = {
-  &sqliteMemMalloc,
-  &sqliteMemFree,
-  &sqliteMemRealloc,
-  &sqliteMemSize,
-  &sqliteMemRoundup,
-  &sqliteMemInit,
-  &sqliteMemShutdown,
-  nullptr
-};
-
-} // namespace
-
-#endif  // MOZ_STORAGE_MEMORY
+const char *GetVFSName();
 
 static const char* sObserverTopics[] = {
   "memory-pressure",
   "xpcom-shutdown",
   "xpcom-shutdown-threads"
 };
 
 nsresult
 Service::initialize()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be initialized on the main thread");
 
-  int rc;
-
-#ifdef MOZ_STORAGE_MEMORY
-  rc = ::sqlite3_config(SQLITE_CONFIG_MALLOC, &memMethods);
-  if (rc != SQLITE_OK)
-    return convertResultCode(rc);
-#endif
-
-  // TODO (bug 1191405): do not preallocate the connections caches until we
-  // have figured the impact on our consumers and memory.
-  sqlite3_config(SQLITE_CONFIG_PAGECACHE, NULL, 0, 0);
-
-  // Explicitly initialize sqlite3.  Although this is implicitly called by
-  // various sqlite3 functions (and the sqlite3_open calls in our case),
-  // the documentation suggests calling this directly.  So we do.
-  rc = ::sqlite3_initialize();
+  int rc = AutoSQLiteLifetime::getInitResult();
   if (rc != SQLITE_OK)
     return convertResultCode(rc);
 
   mSqliteVFS = ConstructTelemetryVFS();
   if (mSqliteVFS) {
-    rc = sqlite3_vfs_register(mSqliteVFS, 1);
+    rc = sqlite3_vfs_register(mSqliteVFS, 0);
     if (rc != SQLITE_OK)
       return convertResultCode(rc);
   } else {
     NS_WARNING("Failed to register telemetry VFS");
   }
 
   // Register for xpcom-shutdown so we can cleanup after ourselves.  The
   // observer service can only be used on the main thread.
new file mode 100644
--- /dev/null
+++ b/toolkit/xre/AutoSQLiteLifetime.cpp
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDebug.h"
+#include "AutoSQLiteLifetime.h"
+#include "sqlite3.h"
+
+#ifdef MOZ_STORAGE_MEMORY
+#  include "mozmemory.h"
+#  ifdef MOZ_DMD
+#    include "DMD.h"
+#  endif
+
+namespace {
+
+// By default, SQLite tracks the size of all its heap blocks by adding an extra
+// 8 bytes at the start of the block to hold the size.  Unfortunately, this
+// causes a lot of 2^N-sized allocations to be rounded up by jemalloc
+// allocator, wasting memory.  For example, a request for 1024 bytes has 8
+// bytes added, becoming a request for 1032 bytes, and jemalloc rounds this up
+// to 2048 bytes, wasting 1012 bytes.  (See bug 676189 for more details.)
+//
+// So we register jemalloc as the malloc implementation, which avoids this
+// 8-byte overhead, and thus a lot of waste.  This requires us to provide a
+// function, sqliteMemRoundup(), which computes the actual size that will be
+// allocated for a given request.  SQLite uses this function before all
+// allocations, and may be able to use any excess bytes caused by the rounding.
+//
+// Note: the wrappers for malloc, realloc and moz_malloc_usable_size are
+// necessary because the sqlite_mem_methods type signatures differ slightly
+// from the standard ones -- they use int instead of size_t.  But we don't need
+// a wrapper for free.
+
+#ifdef MOZ_DMD
+
+// sqlite does its own memory accounting, and we use its numbers in our memory
+// reporters.  But we don't want sqlite's heap blocks to show up in DMD's
+// output as unreported, so we mark them as reported when they're allocated and
+// mark them as unreported when they are freed.
+//
+// In other words, we are marking all sqlite heap blocks as reported even
+// though we're not reporting them ourselves.  Instead we're trusting that
+// sqlite is fully and correctly accounting for all of its heap blocks via its
+// own memory accounting.  Well, we don't have to trust it entirely, because
+// it's easy to keep track (while doing this DMD-specific marking) of exactly
+// how much memory SQLite is using.  And we can compare that against what
+// SQLite reports it is using.
+
+MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(SqliteMallocSizeOfOnAlloc)
+MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(SqliteMallocSizeOfOnFree)
+
+#endif
+
+static void *sqliteMemMalloc(int n)
+{
+  void* p = ::malloc(n);
+#ifdef MOZ_DMD
+  gSqliteMemoryUsed += SqliteMallocSizeOfOnAlloc(p);
+#endif
+  return p;
+}
+
+static void sqliteMemFree(void *p)
+{
+#ifdef MOZ_DMD
+  gSqliteMemoryUsed -= SqliteMallocSizeOfOnFree(p);
+#endif
+  ::free(p);
+}
+
+static void *sqliteMemRealloc(void *p, int n)
+{
+#ifdef MOZ_DMD
+  gSqliteMemoryUsed -= SqliteMallocSizeOfOnFree(p);
+  void *pnew = ::realloc(p, n);
+  if (pnew) {
+    gSqliteMemoryUsed += SqliteMallocSizeOfOnAlloc(pnew);
+  } else {
+    // realloc failed;  undo the SqliteMallocSizeOfOnFree from above
+    gSqliteMemoryUsed += SqliteMallocSizeOfOnAlloc(p);
+  }
+  return pnew;
+#else
+  return ::realloc(p, n);
+#endif
+}
+
+static int sqliteMemSize(void *p)
+{
+  return ::moz_malloc_usable_size(p);
+}
+
+static int sqliteMemRoundup(int n)
+{
+  n = malloc_good_size(n);
+
+  // jemalloc can return blocks of size 2 and 4, but SQLite requires that all
+  // allocations be 8-aligned.  So we round up sub-8 requests to 8.  This
+  // wastes a small amount of memory but is obviously safe.
+  return n <= 8 ? 8 : n;
+}
+
+static int sqliteMemInit(void *p)
+{
+  return 0;
+}
+
+static void sqliteMemShutdown(void *p)
+{
+}
+
+const sqlite3_mem_methods memMethods = {
+  &sqliteMemMalloc,
+  &sqliteMemFree,
+  &sqliteMemRealloc,
+  &sqliteMemSize,
+  &sqliteMemRoundup,
+  &sqliteMemInit,
+  &sqliteMemShutdown,
+  nullptr
+};
+
+} // namespace
+
+#endif  // MOZ_STORAGE_MEMORY
+
+namespace mozilla {
+
+AutoSQLiteLifetime::AutoSQLiteLifetime()
+{
+  if (++AutoSQLiteLifetime::sSingletonEnforcer != 1) {
+    NS_RUNTIMEABORT("multiple instances of AutoSQLiteLifetime constructed!");
+  }
+
+#ifdef MOZ_STORAGE_MEMORY
+  sResult = ::sqlite3_config(SQLITE_CONFIG_MALLOC, &memMethods);
+#else
+  sResult = SQLITE_OK;
+#endif
+
+  if (sResult == SQLITE_OK) {
+    // TODO (bug 1191405): do not preallocate the connections caches until we
+    // have figured the impact on our consumers and memory.
+    sqlite3_config(SQLITE_CONFIG_PAGECACHE, NULL, 0, 0);
+
+    // Explicitly initialize sqlite3.  Although this is implicitly called by
+    // various sqlite3 functions (and the sqlite3_open calls in our case),
+    // the documentation suggests calling this directly.  So we do.
+    sResult = ::sqlite3_initialize();
+  }
+}
+
+AutoSQLiteLifetime::~AutoSQLiteLifetime()
+{
+  // Shutdown the sqlite3 API.  Warn if shutdown did not turn out okay, but
+  // there is nothing actionable we can do in that case.
+  sResult = ::sqlite3_shutdown();
+  NS_WARNING_ASSERTION(sResult == SQLITE_OK,
+                       "sqlite3 did not shutdown cleanly.");
+}
+
+int AutoSQLiteLifetime::sSingletonEnforcer = 0;
+int AutoSQLiteLifetime::sResult = SQLITE_MISUSE;
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/toolkit/xre/AutoSQLiteLifetime.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_AutoSQLiteLifetime_h
+#define mozilla_AutoSQLiteLifetime_h
+
+namespace mozilla {
+
+class AutoSQLiteLifetime final
+{
+private:
+    static int sSingletonEnforcer;
+    static int sResult;
+public:
+    AutoSQLiteLifetime();
+    ~AutoSQLiteLifetime();
+    static int getInitResult() { return AutoSQLiteLifetime::sResult; }
+};
+
+} // namespace mozilla
+
+#endif
--- a/toolkit/xre/Bootstrap.cpp
+++ b/toolkit/xre/Bootstrap.cpp
@@ -1,21 +1,25 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Bootstrap.h"
 #include "nsXPCOM.h"
 
+#include "AutoSQLiteLifetime.h"
+
 namespace mozilla {
 
 class BootstrapImpl final : public Bootstrap
 {
 protected:
+  AutoSQLiteLifetime mSQLLT;
+
   virtual void Dispose() override
   {
     delete this;
   }
 
 public:
   BootstrapImpl()
   {
--- a/toolkit/xre/moz.build
+++ b/toolkit/xre/moz.build
@@ -25,17 +25,17 @@ if CONFIG['OS_ARCH'] == 'WINNT':
 
 XPIDL_MODULE = 'xulapp'
 
 EXPORTS += [
     'nsAppRunner.h',
     'nsIAppStartupNotifier.h',
 ]
 
-EXPORTS.mozilla += ['Bootstrap.h']
+EXPORTS.mozilla += ['AutoSQLiteLifetime.h', 'Bootstrap.h']
 
 if CONFIG['MOZ_INSTRUMENT_EVENT_LOOP']:
     EXPORTS += ['EventTracer.h']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     SOURCES += [
         '../../other-licenses/nsis/Contrib/CityHash/cityhash/city.cpp',
     ]
@@ -86,16 +86,17 @@ if CONFIG['MOZ_X11']:
     ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     UNIFIED_SOURCES += [
         'nsAndroidStartup.cpp',
     ]
 
 UNIFIED_SOURCES += [
+    'AutoSQLiteLifetime.cpp',
     'Bootstrap.cpp',
     'CreateAppData.cpp',
     'nsAppStartupNotifier.cpp',
     'nsConsoleWriter.cpp',
     'nsEmbeddingModule.cpp',
     'nsNativeAppSupportBase.cpp',
     'nsSigHandlers.cpp',
     'nsXREDirProvider.cpp',