/* 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 "BackgroundParentImpl.h" #include "BroadcastChannelParent.h" #include "FileDescriptorSetParent.h" #include "mozilla/media/MediaParent.h" #include "mozilla/AppProcessChecker.h" #include "mozilla/Assertions.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/PBlobParent.h" #include "mozilla/dom/ServiceWorkerRegistrar.h" #include "mozilla/dom/cache/ActorUtils.h" #include "mozilla/dom/indexedDB/ActorsParent.h" #include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/ipc/PBackgroundSharedTypes.h" #include "mozilla/ipc/PBackgroundTestParent.h" #include "mozilla/layout/VsyncParent.h" #include "mozilla/dom/network/UDPSocketParent.h" #include "nsIAppsService.h" #include "nsNetUtil.h" #include "nsRefPtr.h" #include "nsThreadUtils.h" #include "nsTraceRefcnt.h" #include "nsXULAppAPI.h" #ifdef DISABLE_ASSERTS_FOR_FUZZING #define ASSERT_UNLESS_FUZZING(...) do { } while (0) #else #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false) #endif using mozilla::ipc::AssertIsOnBackgroundThread; using mozilla::dom::cache::PCacheParent; using mozilla::dom::cache::PCacheStorageParent; using mozilla::dom::cache::PCacheStreamControlParent; using mozilla::dom::UDPSocketParent; namespace { void AssertIsInMainProcess() { MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); } void AssertIsOnMainThread() { MOZ_ASSERT(NS_IsMainThread()); } class TestParent final : public mozilla::ipc::PBackgroundTestParent { friend class mozilla::ipc::BackgroundParentImpl; TestParent() { MOZ_COUNT_CTOR(TestParent); } protected: ~TestParent() { MOZ_COUNT_DTOR(TestParent); } public: virtual void ActorDestroy(ActorDestroyReason aWhy) override; }; } // anonymous namespace namespace mozilla { namespace ipc { using mozilla::dom::ContentParent; using mozilla::dom::BroadcastChannelParent; using mozilla::dom::ServiceWorkerRegistrationData; BackgroundParentImpl::BackgroundParentImpl() { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_COUNT_CTOR(mozilla::ipc::BackgroundParentImpl); } BackgroundParentImpl::~BackgroundParentImpl() { AssertIsInMainProcess(); AssertIsOnMainThread(); MOZ_COUNT_DTOR(mozilla::ipc::BackgroundParentImpl); } void BackgroundParentImpl::ActorDestroy(ActorDestroyReason aWhy) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); } BackgroundParentImpl::PBackgroundTestParent* BackgroundParentImpl::AllocPBackgroundTestParent(const nsCString& aTestArg) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); return new TestParent(); } bool BackgroundParentImpl::RecvPBackgroundTestConstructor( PBackgroundTestParent* aActor, const nsCString& aTestArg) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); return PBackgroundTestParent::Send__delete__(aActor, aTestArg); } bool BackgroundParentImpl::DeallocPBackgroundTestParent( PBackgroundTestParent* aActor) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); delete static_cast(aActor); return true; } auto BackgroundParentImpl::AllocPBackgroundIDBFactoryParent( const LoggingInfo& aLoggingInfo) -> PBackgroundIDBFactoryParent* { using mozilla::dom::indexedDB::AllocPBackgroundIDBFactoryParent; AssertIsInMainProcess(); AssertIsOnBackgroundThread(); return AllocPBackgroundIDBFactoryParent(aLoggingInfo); } bool BackgroundParentImpl::RecvPBackgroundIDBFactoryConstructor( PBackgroundIDBFactoryParent* aActor, const LoggingInfo& aLoggingInfo) { using mozilla::dom::indexedDB::RecvPBackgroundIDBFactoryConstructor; AssertIsInMainProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); return RecvPBackgroundIDBFactoryConstructor(aActor, aLoggingInfo); } bool BackgroundParentImpl::DeallocPBackgroundIDBFactoryParent( PBackgroundIDBFactoryParent* aActor) { using mozilla::dom::indexedDB::DeallocPBackgroundIDBFactoryParent; AssertIsInMainProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); return DeallocPBackgroundIDBFactoryParent(aActor); } auto BackgroundParentImpl::AllocPBlobParent(const BlobConstructorParams& aParams) -> PBlobParent* { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); if (NS_WARN_IF(aParams.type() != BlobConstructorParams::TParentBlobConstructorParams)) { ASSERT_UNLESS_FUZZING(); return nullptr; } return mozilla::dom::BlobParent::Create(this, aParams); } bool BackgroundParentImpl::DeallocPBlobParent(PBlobParent* aActor) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); mozilla::dom::BlobParent::Destroy(aActor); return true; } PFileDescriptorSetParent* BackgroundParentImpl::AllocPFileDescriptorSetParent( const FileDescriptor& aFileDescriptor) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); return new FileDescriptorSetParent(aFileDescriptor); } bool BackgroundParentImpl::DeallocPFileDescriptorSetParent( PFileDescriptorSetParent* aActor) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); delete static_cast(aActor); return true; } BackgroundParentImpl::PVsyncParent* BackgroundParentImpl::AllocPVsyncParent() { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); nsRefPtr actor = mozilla::layout::VsyncParent::Create(); // There still has one ref-count after return, and it will be released in // DeallocPVsyncParent(). return actor.forget().take(); } bool BackgroundParentImpl::DeallocPVsyncParent(PVsyncParent* aActor) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); // This actor already has one ref-count. Please check AllocPVsyncParent(). nsRefPtr actor = dont_AddRef(static_cast(aActor)); return true; } namespace { class InitUDPSocketParentCallback final : public nsRunnable { public: InitUDPSocketParentCallback(UDPSocketParent* aActor, const nsACString& aFilter) : mActor(aActor) , mFilter(aFilter) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); } NS_IMETHODIMP Run() { AssertIsInMainProcess(); IPC::Principal principal; if (!mActor->Init(principal, mFilter)) { MOZ_CRASH("UDPSocketCallback - failed init"); } return NS_OK; } private: ~InitUDPSocketParentCallback() {}; nsRefPtr mActor; nsCString mFilter; }; } auto BackgroundParentImpl::AllocPUDPSocketParent(const OptionalPrincipalInfo& /* unused */, const nsCString& /* unused */) -> PUDPSocketParent* { nsRefPtr p = new UDPSocketParent(this); return p.forget().take(); } bool BackgroundParentImpl::RecvPUDPSocketConstructor(PUDPSocketParent* aActor, const OptionalPrincipalInfo& aOptionalPrincipal, const nsCString& aFilter) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); if (aOptionalPrincipal.type() == OptionalPrincipalInfo::TPrincipalInfo) { // Support for checking principals (for non-mtransport use) will be handled in // bug 1167039 return false; } // No principal - This must be from mtransport (WebRTC/ICE) - We'd want // to DispatchToMainThread() here, but if we do we must block RecvBind() // until Init() gets run. Since we don't have a principal, and we verify // we have a filter, we can safely skip the Dispatch and just invoke Init() // to install the filter. // For mtransport, this will always be "stun", which doesn't allow outbound packets if // they aren't STUN packets until a STUN response is seen. if (!aFilter.EqualsASCII("stun")) { return false; } IPC::Principal principal; if (!static_cast(aActor)->Init(principal, aFilter)) { MOZ_CRASH("UDPSocketCallback - failed init"); } return true; } bool BackgroundParentImpl::DeallocPUDPSocketParent(PUDPSocketParent* actor) { UDPSocketParent* p = static_cast(actor); p->Release(); return true; } mozilla::dom::PBroadcastChannelParent* BackgroundParentImpl::AllocPBroadcastChannelParent( const PrincipalInfo& aPrincipalInfo, const nsString& aOrigin, const nsString& aChannel, const bool& aPrivateBrowsing) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); return new BroadcastChannelParent(aPrincipalInfo, aOrigin, aChannel, aPrivateBrowsing); } namespace { class CheckPrincipalRunnable final : public nsRunnable { public: CheckPrincipalRunnable(already_AddRefed aParent, const PrincipalInfo& aPrincipalInfo, const nsString& aOrigin) : mContentParent(aParent) , mPrincipalInfo(aPrincipalInfo) , mOrigin(aOrigin) , mBackgroundThread(NS_GetCurrentThread()) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(mContentParent); MOZ_ASSERT(mBackgroundThread); } NS_IMETHODIMP Run() { MOZ_ASSERT(NS_IsMainThread()); struct MOZ_STACK_CLASS RunRAII { explicit RunRAII(nsRefPtr& aContentParent) : mContentParent(aContentParent) {} ~RunRAII() { mContentParent = nullptr; } nsRefPtr& mContentParent; }; RunRAII raii(mContentParent); nsCOMPtr principal = PrincipalInfoToPrincipal(mPrincipalInfo); AssertAppPrincipal(mContentParent, principal); bool isNullPrincipal; nsresult rv = principal->GetIsNullPrincipal(&isNullPrincipal); if (NS_WARN_IF(NS_FAILED(rv)) || isNullPrincipal) { mContentParent->KillHard("BroadcastChannel killed: no null principal."); return NS_OK; } nsCOMPtr uri; rv = NS_NewURI(getter_AddRefs(uri), mOrigin); if (NS_FAILED(rv) || !uri) { mContentParent->KillHard("BroadcastChannel killed: invalid origin URI."); return NS_OK; } rv = principal->CheckMayLoad(uri, false, false); if (NS_FAILED(rv)) { mContentParent->KillHard("BroadcastChannel killed: the url cannot be loaded by the principal."); return NS_OK; } return NS_OK; } private: nsRefPtr mContentParent; PrincipalInfo mPrincipalInfo; nsString mOrigin; nsCOMPtr mBackgroundThread; }; } // anonymous namespace bool BackgroundParentImpl::RecvPBroadcastChannelConstructor( PBroadcastChannelParent* actor, const PrincipalInfo& aPrincipalInfo, const nsString& aOrigin, const nsString& aChannel, const bool& aPrivateBrowsing) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); nsRefPtr parent = BackgroundParent::GetContentParent(this); // If the ContentParent is null we are dealing with a same-process actor. if (!parent) { MOZ_ASSERT(aPrincipalInfo.type() != PrincipalInfo::TNullPrincipalInfo); return true; } nsRefPtr runnable = new CheckPrincipalRunnable(parent.forget(), aPrincipalInfo, aOrigin); nsresult rv = NS_DispatchToMainThread(runnable); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv)); return true; } bool BackgroundParentImpl::DeallocPBroadcastChannelParent( PBroadcastChannelParent* aActor) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); delete static_cast(aActor); return true; } media::PMediaParent* BackgroundParentImpl::AllocPMediaParent() { return media::AllocPMediaParent(); } bool BackgroundParentImpl::DeallocPMediaParent(media::PMediaParent *aActor) { return media::DeallocPMediaParent(aActor); } namespace { class RegisterServiceWorkerCallback final : public nsRunnable { public: explicit RegisterServiceWorkerCallback( const ServiceWorkerRegistrationData& aData) : mData(aData) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); } NS_IMETHODIMP Run() { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); nsRefPtr service = dom::ServiceWorkerRegistrar::Get(); MOZ_ASSERT(service); service->RegisterServiceWorker(mData); return NS_OK; } private: ServiceWorkerRegistrationData mData; }; class UnregisterServiceWorkerCallback final : public nsRunnable { public: UnregisterServiceWorkerCallback(const PrincipalInfo& aPrincipalInfo, const nsString& aScope) : mPrincipalInfo(aPrincipalInfo) , mScope(aScope) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); } NS_IMETHODIMP Run() { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); nsRefPtr service = dom::ServiceWorkerRegistrar::Get(); MOZ_ASSERT(service); service->UnregisterServiceWorker(mPrincipalInfo, NS_ConvertUTF16toUTF8(mScope)); return NS_OK; } private: const PrincipalInfo mPrincipalInfo; nsString mScope; }; class CheckPrincipalWithCallbackRunnable final : public nsRunnable { public: CheckPrincipalWithCallbackRunnable(already_AddRefed aParent, const PrincipalInfo& aPrincipalInfo, nsRunnable* aCallback) : mContentParent(aParent) , mPrincipalInfo(aPrincipalInfo) , mCallback(aCallback) , mBackgroundThread(NS_GetCurrentThread()) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(mContentParent); MOZ_ASSERT(mCallback); MOZ_ASSERT(mBackgroundThread); } NS_IMETHODIMP Run() { if (NS_IsMainThread()) { nsCOMPtr principal = PrincipalInfoToPrincipal(mPrincipalInfo); AssertAppPrincipal(mContentParent, principal); mContentParent = nullptr; mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL); return NS_OK; } AssertIsOnBackgroundThread(); if (mCallback) { mCallback->Run(); mCallback = nullptr; } return NS_OK; } private: nsRefPtr mContentParent; PrincipalInfo mPrincipalInfo; nsRefPtr mCallback; nsCOMPtr mBackgroundThread; }; } // anonymous namespace bool BackgroundParentImpl::RecvRegisterServiceWorker( const ServiceWorkerRegistrationData& aData) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); // Basic validation. if (aData.scope().IsEmpty() || aData.scriptSpec().IsEmpty() || aData.principal().type() == PrincipalInfo::TNullPrincipalInfo || aData.principal().type() == PrincipalInfo::TSystemPrincipalInfo) { return false; } nsRefPtr callback = new RegisterServiceWorkerCallback(aData); nsRefPtr parent = BackgroundParent::GetContentParent(this); // If the ContentParent is null we are dealing with a same-process actor. if (!parent) { callback->Run(); return true; } nsRefPtr runnable = new CheckPrincipalWithCallbackRunnable(parent.forget(), aData.principal(), callback); nsresult rv = NS_DispatchToMainThread(runnable); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv)); return true; } bool BackgroundParentImpl::RecvUnregisterServiceWorker( const PrincipalInfo& aPrincipalInfo, const nsString& aScope) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); // Basic validation. if (aScope.IsEmpty() || aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo || aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { return false; } nsRefPtr callback = new UnregisterServiceWorkerCallback(aPrincipalInfo, aScope); nsRefPtr parent = BackgroundParent::GetContentParent(this); // If the ContentParent is null we are dealing with a same-process actor. if (!parent) { callback->Run(); return true; } nsRefPtr runnable = new CheckPrincipalWithCallbackRunnable(parent.forget(), aPrincipalInfo, callback); nsresult rv = NS_DispatchToMainThread(runnable); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv)); return true; } bool BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar() { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); if (BackgroundParent::IsOtherProcessActor(this)) { return false; } nsRefPtr service = dom::ServiceWorkerRegistrar::Get(); MOZ_ASSERT(service); service->Shutdown(); return true; } PCacheStorageParent* BackgroundParentImpl::AllocPCacheStorageParent(const Namespace& aNamespace, const PrincipalInfo& aPrincipalInfo) { return dom::cache::AllocPCacheStorageParent(this, aNamespace, aPrincipalInfo); } bool BackgroundParentImpl::DeallocPCacheStorageParent(PCacheStorageParent* aActor) { dom::cache::DeallocPCacheStorageParent(aActor); return true; } PCacheParent* BackgroundParentImpl::AllocPCacheParent() { MOZ_CRASH("CacheParent actor must be provided to PBackground manager"); return nullptr; } bool BackgroundParentImpl::DeallocPCacheParent(PCacheParent* aActor) { dom::cache::DeallocPCacheParent(aActor); return true; } PCacheStreamControlParent* BackgroundParentImpl::AllocPCacheStreamControlParent() { MOZ_CRASH("CacheStreamControlParent actor must be provided to PBackground manager"); return nullptr; } bool BackgroundParentImpl::DeallocPCacheStreamControlParent(PCacheStreamControlParent* aActor) { dom::cache::DeallocPCacheStreamControlParent(aActor); return true; } } // namespace ipc } // namespace mozilla void TestParent::ActorDestroy(ActorDestroyReason aWhy) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); }