diff options
Diffstat (limited to '')
-rw-r--r-- | ipc/mscom/COMPtrHolder.h | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/ipc/mscom/COMPtrHolder.h b/ipc/mscom/COMPtrHolder.h new file mode 100644 index 0000000000..e99698b8e7 --- /dev/null +++ b/ipc/mscom/COMPtrHolder.h @@ -0,0 +1,201 @@ +/* -*- 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 <utility> + +#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 <typename Interface, const IID& _IID> +class COMPtrHolder { + public: + typedef ProxyUniquePtr<Interface> COMPtrType; + typedef COMPtrHolder<Interface, _IID> ThisType; + typedef typename detail::EnvironmentSelector<Interface>::Type EnvType; + + COMPtrHolder() {} + + MOZ_IMPLICIT COMPtrHolder(decltype(nullptr)) {} + + explicit COMPtrHolder(COMPtrType&& aPtr) + : mPtr(std::forward<COMPtrType>(aPtr)) {} + + COMPtrHolder(COMPtrType&& aPtr, const ActivationContext& aActCtx) + : mPtr(std::forward<COMPtrType>(aPtr)), mActCtx(aActCtx) {} + + Interface* Get() const { return mPtr.get(); } + + [[nodiscard]] Interface* Release() { return mPtr.release(); } + + void Set(COMPtrType&& aPtr) { mPtr = std::forward<COMPtrType>(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 <typename Interface, const IID& _IID> +struct ParamTraits<mozilla::mscom::COMPtrHolder<Interface, _IID>> { + typedef mozilla::mscom::COMPtrHolder<Interface, _IID> paramType; + + static void Write(MessageWriter* aWriter, 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); + aWriter->WriteInt(bufLen); + if (bufLen) { + aWriter->WriteBytes(reinterpret_cast<const char*>(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(MessageReader* aReader, paramType* aResult) { + int length; + if (!aReader->ReadLength(&length)) { + return false; + } + + mozilla::UniquePtr<BYTE[]> buf; + if (length) { + buf = mozilla::MakeUnique<BYTE[]>(length); + if (!aReader->ReadBytesInto(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 |