Bug 1339947: Make mscom ArrayData lookup respect interface inheritance; r?jimm draft
authorAaron Klotz <aklotz@mozilla.com>
Fri, 10 Feb 2017 14:16:29 -0700
changeset 484872 e798ca6747d2895894d28c763ff8e20750aa7ed2
parent 484871 c91a9b968e4d3e584fe99b9b6536e75abaad1d9c
child 484875 5f6768c670bbb59a66bcd102097655017bc90670
child 484876 faea4d82375812b74405932ef9ac625c83fe8075
push id45576
push useraklotz@mozilla.com
push dateWed, 15 Feb 2017 22:12:01 +0000
reviewersjimm
bugs1339947
milestone54.0a1
Bug 1339947: Make mscom ArrayData lookup respect interface inheritance; r?jimm MozReview-Commit-ID: 5zJTzT7JJhJ
ipc/mscom/Registration.cpp
ipc/mscom/Utils.cpp
ipc/mscom/Utils.h
--- a/ipc/mscom/Registration.cpp
+++ b/ipc/mscom/Registration.cpp
@@ -493,18 +493,19 @@ FindArrayData(REFIID aIid, ULONG aMethod
   if (!sArrayData) {
     return nullptr;
   }
 
   for (auto&& data : *sArrayData) {
     for (size_t innerIdx = 0, innerLen = data.second(); innerIdx < innerLen;
          ++innerIdx) {
       const ArrayData* array = data.first();
-      if (aIid == array[innerIdx].mIid &&
-          aMethodIndex == array[innerIdx].mMethodIndex) {
+      if (aMethodIndex == array[innerIdx].mMethodIndex &&
+          IsInterfaceEqualToOrInheritedFrom(aIid, array[innerIdx].mIid,
+                                            aMethodIndex)) {
         return &array[innerIdx];
       }
     }
   }
 
   return nullptr;
 }
 
--- a/ipc/mscom/Utils.cpp
+++ b/ipc/mscom/Utils.cpp
@@ -1,16 +1,18 @@
 /* -*- 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/Registration.h"
 #include "mozilla/mscom/Utils.h"
 #include "mozilla/RefPtr.h"
+#include "nsTArray.h"
 
 #include <objbase.h>
 #include <objidl.h>
 
 namespace mozilla {
 namespace mscom {
 
 bool
@@ -39,10 +41,138 @@ IsProxy(IUnknown* aUnknown)
   HRESULT hr = aUnknown->QueryInterface(IID_IClientSecurity,
                                         (void**)getter_AddRefs(clientSecurity));
   if (SUCCEEDED(hr) || hr == RPC_E_WRONG_THREAD) {
     return true;
   }
   return false;
 }
 
+static bool
+IsVtableIndexFromParentInterface(TYPEATTR* aTypeAttr,
+                                 unsigned long aVtableIndex)
+{
+  MOZ_ASSERT(aTypeAttr);
+
+  // This is the number of functions declared in this interface (excluding
+  // parent interfaces).
+  unsigned int numExclusiveFuncs = aTypeAttr->cFuncs;
+
+  // This is the number of vtable entries (which includes parent interfaces).
+  // TYPEATTR::cbSizeVft is the entire vtable size in bytes, so we need to
+  // divide in order to compute the number of entries.
+  unsigned int numVtblEntries = aTypeAttr->cbSizeVft / sizeof(void*);
+
+  // This is the index of the first entry in the vtable that belongs to this
+  // interface and not a parent.
+  unsigned int firstVtblIndex = numVtblEntries - numExclusiveFuncs;
+
+  // If aVtableIndex is less than firstVtblIndex, then we're asking for an
+  // index that may belong to a parent interface.
+  return aVtableIndex < firstVtblIndex;
+}
+
+bool
+IsVtableIndexFromParentInterface(REFIID aInterface, unsigned long aVtableIndex)
+{
+  RefPtr<ITypeInfo> typeInfo;
+  if (!RegisteredProxy::Find(aInterface, getter_AddRefs(typeInfo))) {
+    return false;
+  }
+
+  TYPEATTR* typeAttr = nullptr;
+  HRESULT hr = typeInfo->GetTypeAttr(&typeAttr);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  bool result = IsVtableIndexFromParentInterface(typeAttr, aVtableIndex);
+
+  typeInfo->ReleaseTypeAttr(typeAttr);
+  return result;
+}
+
+bool
+IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
+                                  unsigned long aVtableIndexHint)
+{
+  if (aInterface == aFrom) {
+    return true;
+  }
+
+  // We expect this array to be length 1 but that is not guaranteed by the API.
+  AutoTArray<RefPtr<ITypeInfo>, 1> typeInfos;
+
+  // Grab aInterface's ITypeInfo so that we may obtain information about its
+  // inheritance hierarchy.
+  RefPtr<ITypeInfo> typeInfo;
+  if (RegisteredProxy::Find(aInterface, getter_AddRefs(typeInfo))) {
+    typeInfos.AppendElement(Move(typeInfo));
+  }
+
+  /**
+   * The main loop of this function searches the hierarchy of aInterface's
+   * parent interfaces, searching for aFrom.
+   */
+  while (!typeInfos.IsEmpty()) {
+    RefPtr<ITypeInfo> curTypeInfo(Move(typeInfos.LastElement()));
+    typeInfos.RemoveElementAt(typeInfos.Length() - 1);
+
+    TYPEATTR* typeAttr = nullptr;
+    HRESULT hr = curTypeInfo->GetTypeAttr(&typeAttr);
+    if (FAILED(hr)) {
+      break;
+    }
+
+    bool isFromParentVtable = IsVtableIndexFromParentInterface(typeAttr,
+                                                               aVtableIndexHint);
+    WORD numParentInterfaces = typeAttr->cImplTypes;
+
+    curTypeInfo->ReleaseTypeAttr(typeAttr);
+    typeAttr = nullptr;
+
+    if (!isFromParentVtable) {
+      // The vtable index cannot belong to this interface (otherwise the IIDs
+      // would already have matched and we would have returned true). Since we
+      // now also know that the vtable index cannot possibly be contained inside
+      // curTypeInfo's parent interface, there is no point searching any further
+      // up the hierarchy from here. OTOH we still should check any remaining
+      // entries that are still in the typeInfos array, so we continue.
+      continue;
+    }
+
+    for (WORD i = 0; i < numParentInterfaces; ++i) {
+      HREFTYPE refCookie;
+      hr = curTypeInfo->GetRefTypeOfImplType(i, &refCookie);
+      if (FAILED(hr)) {
+        continue;
+      }
+
+      RefPtr<ITypeInfo> nextTypeInfo;
+      hr = curTypeInfo->GetRefTypeInfo(refCookie,
+                                       getter_AddRefs(nextTypeInfo));
+      if (FAILED(hr)) {
+        continue;
+      }
+
+      hr = nextTypeInfo->GetTypeAttr(&typeAttr);
+      if (FAILED(hr)) {
+        continue;
+      }
+
+      IID nextIid = typeAttr->guid;
+
+      nextTypeInfo->ReleaseTypeAttr(typeAttr);
+      typeAttr = nullptr;
+
+      if (nextIid == aFrom) {
+        return true;
+      }
+
+      typeInfos.AppendElement(Move(nextTypeInfo));
+    }
+  }
+
+  return false;
+}
+
 } // namespace mscom
 } // namespace mozilla
--- a/ipc/mscom/Utils.h
+++ b/ipc/mscom/Utils.h
@@ -2,21 +2,27 @@
 /* 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_Utils_h
 #define mozilla_mscom_Utils_h
 
+#include <guiddef.h>
+
 struct IUnknown;
 
 namespace mozilla {
 namespace mscom {
 
 bool IsCurrentThreadMTA();
 bool IsProxy(IUnknown* aUnknown);
+bool IsVtableIndexFromParentInterface(REFIID aInterface,
+                                      unsigned long aVtableIndex);
+bool IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
+                                       unsigned long aVtableIndexHint);
 
 } // namespace mscom
 } // namespace mozilla
 
 #endif // mozilla_mscom_Utils_h