From a583989bb466d5befe4717c4009549d1796e8851 Mon Sep 17 00:00:00 2001 From: Amelia Hart Date: Thu, 29 Jun 2023 19:58:27 +0200 Subject: [PATCH] Prevent potential leak of IndexedDB mDatabaseActor --- dom/indexedDB/ActorsChild.cpp | 25 +++++++++++++++++++++++++ dom/indexedDB/ActorsChild.h | 17 +++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/dom/indexedDB/ActorsChild.cpp b/dom/indexedDB/ActorsChild.cpp index 6ec99e58f6..f4e60004af 100644 --- a/dom/indexedDB/ActorsChild.cpp +++ b/dom/indexedDB/ActorsChild.cpp @@ -1415,6 +1415,7 @@ BackgroundFactoryRequestChild::BackgroundFactoryRequestChild( uint64_t aRequestedVersion) : BackgroundRequestChildBase(aOpenRequest) , mFactory(aFactory) + , mDatabaseActor(nullptr) , mRequestedVersion(aRequestedVersion) , mIsDeleteOp(aIsDeleteOp) { @@ -1439,6 +1440,15 @@ BackgroundFactoryRequestChild::GetOpenDBRequest() const return static_cast(mRequest.get()); } +void +BackgroundFactoryRequestChild::SetDatabaseActor(BackgroundDatabaseChild* aActor) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!aActor || !mDatabaseActor); + + mDatabaseActor = aActor; +} + bool BackgroundFactoryRequestChild::HandleResponse(nsresult aResponse) { @@ -1450,6 +1460,11 @@ BackgroundFactoryRequestChild::HandleResponse(nsresult aResponse) DispatchErrorEvent(mRequest, aResponse); + if (mDatabaseActor) { + mDatabaseActor->ReleaseDOMObject(); + MOZ_ASSERT(!mDatabaseActor); + } + return true; } @@ -1468,6 +1483,7 @@ BackgroundFactoryRequestChild::HandleResponse( IDBDatabase* database = databaseActor->GetDOMObject(); if (!database) { databaseActor->EnsureDOMObject(); + MOZ_ASSERT(mDatabaseActor); database = databaseActor->GetDOMObject(); MOZ_ASSERT(database); @@ -1475,6 +1491,8 @@ BackgroundFactoryRequestChild::HandleResponse( MOZ_ASSERT(!database->IsClosed()); } + MOZ_ASSERT(mDatabaseActor == databaseActor); + if (database->IsClosed()) { // If the database was closed already, which is only possible if we fired an // "upgradeneeded" event, then we shouldn't fire a "success" event here. @@ -1487,6 +1505,7 @@ BackgroundFactoryRequestChild::HandleResponse( } databaseActor->ReleaseDOMObject(); + MOZ_ASSERT(!mDatabaseActor); return true; } @@ -1507,6 +1526,8 @@ BackgroundFactoryRequestChild::HandleResponse( DispatchSuccessEvent(&helper, successEvent); + MOZ_ASSERT(!mDatabaseActor); + return true; } @@ -1737,6 +1758,8 @@ BackgroundDatabaseChild::EnsureDOMObject() mDatabase = mTemporaryStrongDatabase; mSpec.forget(); + + mOpenRequestActor->SetDatabaseActor(this); } void @@ -1748,6 +1771,8 @@ BackgroundDatabaseChild::ReleaseDOMObject() MOZ_ASSERT(mOpenRequestActor); MOZ_ASSERT(mDatabase == mTemporaryStrongDatabase); + mOpenRequestActor->SetDatabaseActor(nullptr); + mOpenRequestActor = nullptr; // This may be the final reference to the IDBDatabase object so we may end up diff --git a/dom/indexedDB/ActorsChild.h b/dom/indexedDB/ActorsChild.h index 1a59993898..20dc4667b1 100644 --- a/dom/indexedDB/ActorsChild.h +++ b/dom/indexedDB/ActorsChild.h @@ -254,6 +254,20 @@ class BackgroundFactoryRequestChild final friend class PermissionRequestParent; RefPtr mFactory; + + // Normally when opening of a database is successful, we receive a database + // actor in request response, so we can use it to call ReleaseDOMObject() + // which clears temporary strong reference to IDBDatabase. + // However, when there's an error, we don't receive a database actor and + // IDBRequest::mTransaction is already cleared (must be). So the only way how + // to call ReleaseDOMObject() is to have a back-reference to database actor. + // This creates a weak ref cycle between + // BackgroundFactoryRequestChild (using mDatabaseActor member) and + // BackgroundDatabaseChild actor (using mOpenRequestActor member). + // mDatabaseActor is set in EnsureDOMObject() and cleared in + // ReleaseDOMObject(). + BackgroundDatabaseChild* mDatabaseActor; + const uint64_t mRequestedVersion; const bool mIsDeleteOp; @@ -271,6 +285,9 @@ private: // Only destroyed by BackgroundFactoryChild. ~BackgroundFactoryRequestChild(); + void + SetDatabaseActor(BackgroundDatabaseChild* aActor); + bool HandleResponse(nsresult aResponse);