/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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_mscom_COMPtrHolder_h #define mozilla_mscom_COMPtrHolder_h #include #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/DebugOnly.h" #include "mozilla/mscom/ProxyStream.h" #include "mozilla/mscom/Ptr.h" #if defined(MOZ_SANDBOX) # include "mozilla/SandboxSettings.h" #endif // defined(MOZ_SANDBOX) #include "nsExceptionHandler.h" namespace mozilla { namespace mscom { template class COMPtrHolder { public: typedef ProxyUniquePtr COMPtrType; typedef COMPtrHolder ThisType; typedef typename detail::EnvironmentSelector::Type EnvType; COMPtrHolder() {} MOZ_IMPLICIT COMPtrHolder(decltype(nullptr)) {} explicit COMPtrHolder(COMPtrType&& aPtr) : mPtr(std::forward(aPtr)) {} COMPtrHolder(COMPtrType&& aPtr, const ActivationContext& aActCtx) : mPtr(std::forward(aPtr)), mActCtx(aActCtx) {} Interface* Get() const { return mPtr.get(); } [[nodiscard]] Interface* Release() { return mPtr.release(); } void Set(COMPtrType&& aPtr) { mPtr = std::forward(aPtr); } void SetActCtx(const ActivationContext& aActCtx) { mActCtx = aActCtx; } #if defined(MOZ_SANDBOX) // This method is const because we need to call it during IPC write, where // we are passed as a const argument. At higher sandboxing levels we need to // save this artifact from the serialization process for later deletion. void PreserveStream(PreservedStreamPtr aPtr) const { MOZ_ASSERT(!mMarshaledStream); mMarshaledStream = std::move(aPtr); } PreservedStreamPtr GetPreservedStream() { return std::move(mMarshaledStream); } #endif // defined(MOZ_SANDBOX) COMPtrHolder(const COMPtrHolder& aOther) = delete; COMPtrHolder(COMPtrHolder&& aOther) : mPtr(std::move(aOther.mPtr)) #if defined(MOZ_SANDBOX) , mMarshaledStream(std::move(aOther.mMarshaledStream)) #endif // defined(MOZ_SANDBOX) { } // COMPtrHolder is eventually added as a member of a struct that is declared // in IPDL. The generated C++ code for that IPDL struct includes copy // constructors and assignment operators that assume that all members are // copyable. I don't think that those copy constructors and operator= are // actually used by any generated code, but they are made available. Since no // move semantics are available, this terrible hack makes COMPtrHolder build // when used as a member of an IPDL struct. ThisType& operator=(const ThisType& aOther) { Set(std::move(aOther.mPtr)); #if defined(MOZ_SANDBOX) mMarshaledStream = std::move(aOther.mMarshaledStream); #endif // defined(MOZ_SANDBOX) return *this; } ThisType& operator=(ThisType&& aOther) { Set(std::move(aOther.mPtr)); #if defined(MOZ_SANDBOX) mMarshaledStream = std::move(aOther.mMarshaledStream); #endif // defined(MOZ_SANDBOX) return *this; } bool operator==(const ThisType& aOther) const { return mPtr == aOther.mPtr; } bool IsNull() const { return !mPtr; } private: // This is mutable to facilitate the above operator= hack mutable COMPtrType mPtr; ActivationContext mActCtx; #if defined(MOZ_SANDBOX) // This is mutable so that we may optionally store a reference to a marshaled // stream to be cleaned up later via PreserveStream(). mutable PreservedStreamPtr mMarshaledStream; #endif // defined(MOZ_SANDBOX) }; } // namespace mscom } // namespace mozilla namespace IPC { template struct ParamTraits> { typedef mozilla::mscom::COMPtrHolder paramType; static void Write(Message* aMsg, const paramType& aParam) { #if defined(MOZ_SANDBOX) static const bool sIsStreamPreservationNeeded = XRE_IsParentProcess() && mozilla::GetEffectiveContentSandboxLevel() >= 3; #else const bool sIsStreamPreservationNeeded = false; #endif // defined(MOZ_SANDBOX) typename paramType::EnvType env; mozilla::mscom::ProxyStreamFlags flags = sIsStreamPreservationNeeded ? mozilla::mscom::ProxyStreamFlags::ePreservable : mozilla::mscom::ProxyStreamFlags::eDefault; mozilla::mscom::ProxyStream proxyStream(_IID, aParam.Get(), &env, flags); int bufLen; const BYTE* buf = proxyStream.GetBuffer(bufLen); MOZ_ASSERT(buf || !bufLen); aMsg->WriteInt(bufLen); if (bufLen) { aMsg->WriteBytes(reinterpret_cast(buf), bufLen); } #if defined(MOZ_SANDBOX) if (sIsStreamPreservationNeeded) { /** * When we're sending a ProxyStream from parent to content and the * content sandboxing level is >= 3, content is unable to communicate * its releasing of its reference to the proxied object. We preserve the * marshaled proxy data here and later manually release it on content's * behalf. */ aParam.PreserveStream(proxyStream.GetPreservedStream()); } #endif // defined(MOZ_SANDBOX) } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { int length; if (!aMsg->ReadLength(aIter, &length)) { return false; } mozilla::UniquePtr buf; if (length) { buf = mozilla::MakeUnique(length); if (!aMsg->ReadBytesInto(aIter, buf.get(), length)) { return false; } } typename paramType::EnvType env; mozilla::mscom::ProxyStream proxyStream(_IID, buf.get(), length, &env); if (!proxyStream.IsValid()) { CrashReporter::AnnotateCrashReport( CrashReporter::Annotation::ProxyStreamValid, "false"_ns); return false; } typename paramType::COMPtrType ptr; if (!proxyStream.GetInterface(mozilla::mscom::getter_AddRefs(ptr))) { return false; } aResult->Set(std::move(ptr)); return true; } }; } // namespace IPC #endif // mozilla_mscom_COMPtrHolder_h