summaryrefslogtreecommitdiffstats
path: root/ipc/mscom/ProxyStream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/mscom/ProxyStream.cpp')
-rw-r--r--ipc/mscom/ProxyStream.cpp404
1 files changed, 404 insertions, 0 deletions
diff --git a/ipc/mscom/ProxyStream.cpp b/ipc/mscom/ProxyStream.cpp
new file mode 100644
index 0000000000..f1af6c75a7
--- /dev/null
+++ b/ipc/mscom/ProxyStream.cpp
@@ -0,0 +1,404 @@
+/* -*- 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/. */
+
+#include <utility>
+#if defined(ACCESSIBILITY)
+# include "HandlerData.h"
+# include "mozilla/a11y/Platform.h"
+# include "mozilla/mscom/ActivationContext.h"
+#endif // defined(ACCESSIBILITY)
+#include "mozilla/mscom/EnsureMTA.h"
+#include "mozilla/mscom/ProxyStream.h"
+#include "mozilla/mscom/Utils.h"
+#include "mozilla/ScopeExit.h"
+
+#include "mozilla/mscom/Objref.h"
+#include "nsExceptionHandler.h"
+#include "nsPrintfCString.h"
+#include "RegistrationAnnotator.h"
+
+#include <windows.h>
+#include <objbase.h>
+#include <shlwapi.h>
+
+namespace mozilla {
+namespace mscom {
+
+ProxyStream::ProxyStream()
+ : mGlobalLockedBuf(nullptr),
+ mHGlobal(nullptr),
+ mBufSize(0),
+ mPreserveStream(false) {}
+
+// GetBuffer() fails with this variant, but that's okay because we're just
+// reconstructing the stream from a buffer anyway.
+ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf,
+ const int aInitBufSize, Environment* aEnv)
+ : mGlobalLockedBuf(nullptr),
+ mHGlobal(nullptr),
+ mBufSize(aInitBufSize),
+ mPreserveStream(false) {
+ CrashReporter::Annotation kCrashReportKey =
+ CrashReporter::Annotation::ProxyStreamUnmarshalStatus;
+
+ if (!aInitBufSize) {
+ CrashReporter::AnnotateCrashReport(kCrashReportKey, "!aInitBufSize"_ns);
+ // We marshaled a nullptr. Nothing else to do here.
+ return;
+ }
+
+ HRESULT createStreamResult =
+ CreateStream(aInitBuf, aInitBufSize, getter_AddRefs(mStream));
+ if (FAILED(createStreamResult)) {
+ nsPrintfCString hrAsStr("0x%08X", createStreamResult);
+ CrashReporter::AnnotateCrashReport(kCrashReportKey, hrAsStr);
+ return;
+ }
+
+ // NB: We can't check for a null mStream until after we have checked for
+ // the zero aInitBufSize above. This is because InitStream will also fail
+ // in that case, even though marshaling a nullptr is allowable.
+ MOZ_ASSERT(mStream);
+ if (!mStream) {
+ CrashReporter::AnnotateCrashReport(kCrashReportKey, "!mStream"_ns);
+ return;
+ }
+
+#if defined(ACCESSIBILITY)
+ const uint32_t expectedStreamLen = GetOBJREFSize(WrapNotNull(mStream));
+ nsAutoCString strActCtx;
+ nsAutoString manifestPath;
+#endif // defined(ACCESSIBILITY)
+
+ HRESULT unmarshalResult = S_OK;
+
+ // We need to convert to an interface here otherwise we mess up const
+ // correctness with IPDL. We'll request an IUnknown and then QI the
+ // actual interface later.
+
+#if defined(ACCESSIBILITY)
+ auto marshalFn = [this, &strActCtx, &manifestPath, &unmarshalResult, &aIID,
+ aEnv]() -> void
+#else
+ auto marshalFn = [this, &unmarshalResult, &aIID, aEnv]() -> void
+#endif // defined(ACCESSIBILITY)
+ {
+ if (aEnv) {
+ bool pushOk = aEnv->Push();
+ MOZ_DIAGNOSTIC_ASSERT(pushOk);
+ if (!pushOk) {
+ return;
+ }
+ }
+
+ auto popEnv = MakeScopeExit([aEnv]() -> void {
+ if (!aEnv) {
+ return;
+ }
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ bool popOk =
+#endif
+ aEnv->Pop();
+ MOZ_DIAGNOSTIC_ASSERT(popOk);
+ });
+
+#if defined(ACCESSIBILITY)
+ auto curActCtx = ActivationContext::GetCurrent();
+ if (curActCtx.isOk()) {
+ strActCtx.AppendPrintf("0x%p", curActCtx.unwrap());
+ } else {
+ strActCtx.AppendPrintf("HRESULT 0x%08X", curActCtx.unwrapErr());
+ }
+
+ ActivationContext::GetCurrentManifestPath(manifestPath);
+#endif // defined(ACCESSIBILITY)
+
+ unmarshalResult = ::CoUnmarshalInterface(mStream, aIID,
+ getter_AddRefs(mUnmarshaledProxy));
+ MOZ_ASSERT(SUCCEEDED(unmarshalResult));
+ };
+
+ if (XRE_IsParentProcess()) {
+ // We'll marshal this stuff directly using the current thread, therefore its
+ // proxy will reside in the same apartment as the current thread.
+ marshalFn();
+ } else {
+ // When marshaling in child processes, we want to force the MTA.
+ EnsureMTA mta(marshalFn);
+ }
+
+ mStream = nullptr;
+
+ if (FAILED(unmarshalResult) || !mUnmarshaledProxy) {
+ nsPrintfCString hrAsStr("0x%08X", unmarshalResult);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::CoUnmarshalInterfaceResult, hrAsStr);
+ AnnotateInterfaceRegistration(aIID);
+ if (!mUnmarshaledProxy) {
+ CrashReporter::AnnotateCrashReport(kCrashReportKey,
+ "!mUnmarshaledProxy"_ns);
+ }
+
+#if defined(ACCESSIBILITY)
+ AnnotateClassRegistration(CLSID_AccessibleHandler);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::UnmarshalActCtx, strActCtx);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::UnmarshalActCtxManifestPath,
+ NS_ConvertUTF16toUTF8(manifestPath));
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::A11yHandlerRegistered,
+ a11y::IsHandlerRegistered() ? "true"_ns : "false"_ns);
+
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::ExpectedStreamLen, expectedStreamLen);
+
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::ActualStreamLen, aInitBufSize);
+#endif // defined(ACCESSIBILITY)
+ }
+}
+
+ProxyStream::ProxyStream(ProxyStream&& aOther)
+ : mGlobalLockedBuf(nullptr),
+ mHGlobal(nullptr),
+ mBufSize(0),
+ mPreserveStream(false) {
+ *this = std::move(aOther);
+}
+
+ProxyStream& ProxyStream::operator=(ProxyStream&& aOther) {
+ if (mHGlobal && mGlobalLockedBuf) {
+ DebugOnly<BOOL> result = ::GlobalUnlock(mHGlobal);
+ MOZ_ASSERT(!result && ::GetLastError() == NO_ERROR);
+ }
+
+ mStream = std::move(aOther.mStream);
+
+ mGlobalLockedBuf = aOther.mGlobalLockedBuf;
+ aOther.mGlobalLockedBuf = nullptr;
+
+ // ::GlobalFree() was called implicitly when mStream was replaced.
+ mHGlobal = aOther.mHGlobal;
+ aOther.mHGlobal = nullptr;
+
+ mBufSize = aOther.mBufSize;
+ aOther.mBufSize = 0;
+
+ mUnmarshaledProxy = std::move(aOther.mUnmarshaledProxy);
+
+ mPreserveStream = aOther.mPreserveStream;
+ return *this;
+}
+
+ProxyStream::~ProxyStream() {
+ if (mHGlobal && mGlobalLockedBuf) {
+ DebugOnly<BOOL> result = ::GlobalUnlock(mHGlobal);
+ MOZ_ASSERT(!result && ::GetLastError() == NO_ERROR);
+ // ::GlobalFree() is called implicitly when mStream is released
+ }
+
+ // If this assert triggers then we will be leaking a marshaled proxy!
+ // Call GetPreservedStream to obtain a preservable stream and then save it
+ // until the proxy is no longer needed.
+ MOZ_ASSERT(!mPreserveStream);
+}
+
+const BYTE* ProxyStream::GetBuffer(int& aReturnedBufSize) const {
+ aReturnedBufSize = 0;
+ if (!mStream) {
+ return nullptr;
+ }
+ if (!mGlobalLockedBuf) {
+ return nullptr;
+ }
+ aReturnedBufSize = mBufSize;
+ return mGlobalLockedBuf;
+}
+
+PreservedStreamPtr ProxyStream::GetPreservedStream() {
+ MOZ_ASSERT(mStream);
+ MOZ_ASSERT(mHGlobal);
+
+ if (!mStream || !mPreserveStream) {
+ return nullptr;
+ }
+
+ // Clone the stream so that the result has a distinct seek pointer.
+ RefPtr<IStream> cloned;
+ HRESULT hr = mStream->Clone(getter_AddRefs(cloned));
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ // Ensure the stream is rewound. We do this because CoReleaseMarshalData needs
+ // the stream to be pointing to the beginning of the marshal data.
+ LARGE_INTEGER pos;
+ pos.QuadPart = 0LL;
+ hr = cloned->Seek(pos, STREAM_SEEK_SET, nullptr);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ mPreserveStream = false;
+ return ToPreservedStreamPtr(std::move(cloned));
+}
+
+bool ProxyStream::GetInterface(void** aOutInterface) {
+ // We should not have a locked buffer on this side
+ MOZ_ASSERT(!mGlobalLockedBuf);
+ MOZ_ASSERT(aOutInterface);
+
+ if (!aOutInterface) {
+ return false;
+ }
+
+ *aOutInterface = mUnmarshaledProxy.release();
+ return true;
+}
+
+ProxyStream::ProxyStream(REFIID aIID, IUnknown* aObject, Environment* aEnv,
+ ProxyStreamFlags aFlags)
+ : mGlobalLockedBuf(nullptr),
+ mHGlobal(nullptr),
+ mBufSize(0),
+ mPreserveStream(aFlags & ProxyStreamFlags::ePreservable) {
+ if (!aObject) {
+ return;
+ }
+
+ RefPtr<IStream> stream;
+ HGLOBAL hglobal = NULL;
+ int streamSize = 0;
+ DWORD mshlFlags = mPreserveStream ? MSHLFLAGS_TABLESTRONG : MSHLFLAGS_NORMAL;
+
+ HRESULT createStreamResult = S_OK;
+ HRESULT marshalResult = S_OK;
+ HRESULT statResult = S_OK;
+ HRESULT getHGlobalResult = S_OK;
+
+ nsAutoString manifestPath;
+
+ auto marshalFn = [&aIID, aObject, mshlFlags, &stream, &streamSize, &hglobal,
+ &createStreamResult, &marshalResult, &statResult,
+ &getHGlobalResult, aEnv, &manifestPath]() -> void {
+ if (aEnv) {
+ bool pushOk = aEnv->Push();
+ MOZ_DIAGNOSTIC_ASSERT(pushOk);
+ if (!pushOk) {
+ return;
+ }
+ }
+
+ auto popEnv = MakeScopeExit([aEnv]() -> void {
+ if (!aEnv) {
+ return;
+ }
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ bool popOk =
+#endif
+ aEnv->Pop();
+ MOZ_DIAGNOSTIC_ASSERT(popOk);
+ });
+
+ createStreamResult =
+ ::CreateStreamOnHGlobal(nullptr, TRUE, getter_AddRefs(stream));
+ if (FAILED(createStreamResult)) {
+ return;
+ }
+
+#if defined(ACCESSIBILITY)
+ ActivationContext::GetCurrentManifestPath(manifestPath);
+#endif // defined(ACCESSIBILITY)
+
+ marshalResult = ::CoMarshalInterface(stream, aIID, aObject, MSHCTX_LOCAL,
+ nullptr, mshlFlags);
+ MOZ_ASSERT(marshalResult != E_INVALIDARG);
+ if (FAILED(marshalResult)) {
+ return;
+ }
+
+ STATSTG statstg;
+ statResult = stream->Stat(&statstg, STATFLAG_NONAME);
+ if (SUCCEEDED(statResult)) {
+ streamSize = static_cast<int>(statstg.cbSize.LowPart);
+ } else {
+ return;
+ }
+
+ getHGlobalResult = ::GetHGlobalFromStream(stream, &hglobal);
+ MOZ_ASSERT(SUCCEEDED(getHGlobalResult));
+ };
+
+ if (XRE_IsParentProcess()) {
+ // We'll marshal this stuff directly using the current thread, therefore its
+ // stub will reside in the same apartment as the current thread.
+ marshalFn();
+ } else {
+ // When marshaling in child processes, we want to force the MTA.
+ EnsureMTA mta(marshalFn);
+ }
+
+ if (FAILED(createStreamResult)) {
+ nsPrintfCString hrAsStr("0x%08X", createStreamResult);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::CreateStreamOnHGlobalFailure, hrAsStr);
+ }
+
+ if (FAILED(marshalResult)) {
+ AnnotateInterfaceRegistration(aIID);
+ nsPrintfCString hrAsStr("0x%08X", marshalResult);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::CoMarshalInterfaceFailure, hrAsStr);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::MarshalActCtxManifestPath,
+ NS_ConvertUTF16toUTF8(manifestPath));
+ }
+
+ if (FAILED(statResult)) {
+ nsPrintfCString hrAsStr("0x%08X", statResult);
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::StatFailure,
+ hrAsStr);
+ }
+
+ if (FAILED(getHGlobalResult)) {
+ nsPrintfCString hrAsStr("0x%08X", getHGlobalResult);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::GetHGlobalFromStreamFailure, hrAsStr);
+ }
+
+ mStream = std::move(stream);
+
+ if (streamSize) {
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::ProxyStreamSizeFrom, "IStream::Stat"_ns);
+ mBufSize = streamSize;
+ }
+
+ if (!hglobal) {
+ return;
+ }
+
+ mGlobalLockedBuf = reinterpret_cast<BYTE*>(::GlobalLock(hglobal));
+ mHGlobal = hglobal;
+
+ // If we couldn't get the stream size directly from mStream, we may use
+ // the size of the memory block allocated by the HGLOBAL, though it might
+ // be larger than the actual stream size.
+ if (!streamSize) {
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::ProxyStreamSizeFrom, "GlobalSize"_ns);
+ mBufSize = static_cast<int>(::GlobalSize(hglobal));
+ }
+
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::ProxyStreamSize,
+ mBufSize);
+}
+
+} // namespace mscom
+} // namespace mozilla