Bug 1277075: Add MainThreadRuntime class to mscom glue, enabling safe initialization of COM security and exception handlers; r=jimm
authorAaron Klotz <aklotz@mozilla.com>
Wed, 20 Jul 2016 11:10:46 -0600
changeset 345866 c2d21e0b1856fb456f2edade165226c8fa0789d1
parent 345865 a8950de62b29d5ebe6fa30ef65ef78e519d8ea34
child 345867 6867c82fc64d3cf763bc7dcff2ed42f96c9c0dfe
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1277075
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1277075: Add MainThreadRuntime class to mscom glue, enabling safe initialization of COM security and exception handlers; r=jimm MozReview-Commit-ID: 9Ig9wb4KVps
ipc/mscom/COMApartmentRegion.h
ipc/mscom/MainThreadRuntime.cpp
ipc/mscom/MainThreadRuntime.h
ipc/mscom/moz.build
--- a/ipc/mscom/COMApartmentRegion.h
+++ b/ipc/mscom/COMApartmentRegion.h
@@ -11,17 +11,17 @@
 #include "mozilla/Attributes.h"
 
 #include <objbase.h>
 
 namespace mozilla {
 namespace mscom {
 
 template<COINIT T>
-class MOZ_RAII COMApartmentRegion
+class MOZ_NON_TEMPORARY_CLASS COMApartmentRegion
 {
 public:
   COMApartmentRegion()
     : mInitResult(::CoInitializeEx(nullptr, T))
   {
     // If this fires then we're probably mixing apartments on the same thread
     MOZ_ASSERT(IsValid());
   }
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/MainThreadRuntime.cpp
@@ -0,0 +1,166 @@
+/* -*- 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 "mozilla/mscom/MainThreadRuntime.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/WindowsVersion.h"
+#include "nsDebug.h"
+#include "nsWindowsHelpers.h"
+
+#include <accctrl.h>
+#include <aclapi.h>
+#include <objbase.h>
+#include <objidl.h>
+
+namespace {
+
+struct LocalFreeDeleter
+{
+  void operator()(void* aPtr)
+  {
+    ::LocalFree(aPtr);
+  }
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace mscom {
+
+MainThreadRuntime::MainThreadRuntime()
+  : mInitResult(E_UNEXPECTED)
+{
+  // We must be the outermost COM initialization on this thread. The COM runtime
+  // cannot be configured once we start manipulating objects
+  MOZ_ASSERT(mStaRegion.IsValidOutermost());
+  if (NS_WARN_IF(!mStaRegion.IsValidOutermost())) {
+    return;
+  }
+
+  // Windows XP doesn't support setting of the COM exception policy, so we'll
+  // just stop here in that case.
+  if (!IsVistaOrLater()) {
+    mInitResult = S_OK;
+    return;
+  }
+
+  // We are required to initialize security in order to configure global options.
+  mInitResult = InitializeSecurity();
+  MOZ_ASSERT(SUCCEEDED(mInitResult));
+  if (FAILED(mInitResult)) {
+    return;
+  }
+
+  RefPtr<IGlobalOptions> globalOpts;
+  mInitResult = ::CoCreateInstance(CLSID_GlobalOptions, nullptr,
+                                   CLSCTX_INPROC_SERVER, IID_IGlobalOptions,
+                                   (void**)getter_AddRefs(globalOpts));
+  MOZ_ASSERT(SUCCEEDED(mInitResult));
+  if (FAILED(mInitResult)) {
+    return;
+  }
+
+  // Windows 7 has a policy that is even more strict. We should use that one
+  // whenever possible.
+  ULONG_PTR exceptionSetting = IsWin7OrLater() ?
+                               COMGLB_EXCEPTION_DONOT_HANDLE_ANY :
+                               COMGLB_EXCEPTION_DONOT_HANDLE;
+  mInitResult = globalOpts->Set(COMGLB_EXCEPTION_HANDLING, exceptionSetting);
+  MOZ_ASSERT(SUCCEEDED(mInitResult));
+}
+
+HRESULT
+MainThreadRuntime::InitializeSecurity()
+{
+  HANDLE rawToken = nullptr;
+  BOOL ok = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken);
+  if (!ok) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+  nsAutoHandle token(rawToken);
+
+  DWORD len = 0;
+  ok = ::GetTokenInformation(token, TokenUser, nullptr, len, &len);
+  DWORD win32Error = ::GetLastError();
+  if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
+    return HRESULT_FROM_WIN32(win32Error);
+  }
+
+  auto tokenUserBuf = MakeUnique<BYTE[]>(len);
+  TOKEN_USER& tokenUser = *reinterpret_cast<TOKEN_USER*>(tokenUserBuf.get());
+  ok = ::GetTokenInformation(token, TokenUser, tokenUserBuf.get(), len, &len);
+  if (!ok) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  len = 0;
+  ok = ::GetTokenInformation(token, TokenPrimaryGroup, nullptr, len, &len);
+  win32Error = ::GetLastError();
+  if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
+    return HRESULT_FROM_WIN32(win32Error);
+  }
+
+  auto tokenPrimaryGroupBuf = MakeUnique<BYTE[]>(len);
+  TOKEN_PRIMARY_GROUP& tokenPrimaryGroup =
+    *reinterpret_cast<TOKEN_PRIMARY_GROUP*>(tokenPrimaryGroupBuf.get());
+  ok = ::GetTokenInformation(token, TokenPrimaryGroup, tokenPrimaryGroupBuf.get(),
+                             len, &len);
+  if (!ok) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  SECURITY_DESCRIPTOR sd;
+  if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  // Grant access to SYSTEM, Administrators, and the user.
+  EXPLICIT_ACCESS entries[] = {
+    {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
+      {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME, TRUSTEE_IS_USER,
+       L"SYSTEM"}},
+    {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
+      {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME, TRUSTEE_IS_WELL_KNOWN_GROUP,
+       L"ADMINISTRATORS"}},
+    {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
+      {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
+       reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}}
+  };
+
+  PACL rawDacl = nullptr;
+  win32Error = ::SetEntriesInAcl(ArrayLength(entries), entries, nullptr,
+                                 &rawDacl);
+  if (win32Error != ERROR_SUCCESS) {
+    return HRESULT_FROM_WIN32(win32Error);
+  }
+
+  UniquePtr<ACL, LocalFreeDeleter> dacl(rawDacl);
+
+  if (!::SetSecurityDescriptorDacl(&sd, TRUE, dacl.get(), FALSE)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  if (!::SetSecurityDescriptorOwner(&sd, tokenUser.User.Sid, FALSE)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  if (!::SetSecurityDescriptorGroup(&sd, tokenPrimaryGroup.PrimaryGroup, FALSE)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  return ::CoInitializeSecurity(&sd, -1, nullptr, nullptr,
+                                RPC_C_AUTHN_LEVEL_DEFAULT,
+                                RPC_C_IMP_LEVEL_IDENTIFY, nullptr, EOAC_NONE,
+                                nullptr);
+}
+
+} // namespace mscom
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/MainThreadRuntime.h
@@ -0,0 +1,42 @@
+/* -*- 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_MainThreadRuntime_h
+#define mozilla_mscom_MainThreadRuntime_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/mscom/COMApartmentRegion.h"
+
+namespace mozilla {
+namespace mscom {
+
+class MOZ_NON_TEMPORARY_CLASS MainThreadRuntime
+{
+public:
+  MainThreadRuntime();
+
+  explicit operator bool() const
+  {
+    return mStaRegion.IsValidOutermost() && SUCCEEDED(mInitResult);
+  }
+
+  MainThreadRuntime(MainThreadRuntime&) = delete;
+  MainThreadRuntime(MainThreadRuntime&&) = delete;
+  MainThreadRuntime& operator=(MainThreadRuntime&) = delete;
+  MainThreadRuntime& operator=(MainThreadRuntime&&) = delete;
+
+private:
+  HRESULT InitializeSecurity();
+
+  STARegion mStaRegion;
+  HRESULT mInitResult;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_MainThreadRuntime_h
+
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -1,18 +1,20 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 EXPORTS.mozilla.mscom += [
     'COMApartmentRegion.h',
+    'MainThreadRuntime.h',
     'Utils.h',
 ]
 
 UNIFIED_SOURCES += [
+    'MainThreadRuntime.cpp',
     'Utils.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'