Bug 1315543 - Eliminate UAF in Navigator::NotifyVRDisplaysUpdated,r=dmu
authorKearwood (Kip) Gilbert <kgilbert@mozilla.com>
Thu, 24 Nov 2016 11:50:43 -0800
changeset 324271 bb16059022c621e095d2e013a1f14088e0055714
parent 324270 b2e450275deefe6855206ae14493a89dbe68977d
child 324272 8c6411235af992640066af2086363c92e5344eb3
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersdmu
bugs1315543
milestone53.0a1
Bug 1315543 - Eliminate UAF in Navigator::NotifyVRDisplaysUpdated,r=dmu
dom/base/Navigator.cpp
dom/vr/VRDisplay.cpp
dom/vr/VRDisplay.h
gfx/vr/ipc/VRManagerChild.cpp
gfx/vr/ipc/VRManagerChild.h
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1642,19 +1642,19 @@ Navigator::GetVRDisplays(ErrorResult& aR
   win->NotifyVREventListenerAdded();
 
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
   RefPtr<Promise> p = Promise::Create(go, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  // We pass ourself to RefreshVRDisplays, so NotifyVRDisplaysUpdated will
+  // We pass mWindow's id to RefreshVRDisplays, so NotifyVRDisplaysUpdated will
   // be called asynchronously, resolving the promises in mVRGetDisplaysPromises.
-  if (!VRDisplay::RefreshVRDisplays(this)) {
+  if (!VRDisplay::RefreshVRDisplays(win->WindowID())) {
     p->MaybeReject(NS_ERROR_FAILURE);
     return p.forget();
   }
 
   mVRGetDisplaysPromises.AppendElement(p);
   return p.forget();
 }
 
--- a/dom/vr/VRDisplay.cpp
+++ b/dom/vr/VRDisplay.cpp
@@ -72,20 +72,20 @@ VRDisplayCapabilities::CanPresent() cons
 
 uint32_t
 VRDisplayCapabilities::MaxLayers() const
 {
   return CanPresent() ? 1 : 0;
 }
 
 /*static*/ bool
-VRDisplay::RefreshVRDisplays(dom::Navigator* aNavigator)
+VRDisplay::RefreshVRDisplays(uint64_t aWindowId)
 {
   gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
-  return vm && vm->RefreshVRDisplaysWithCallback(aNavigator);
+  return vm && vm->RefreshVRDisplaysWithCallback(aWindowId);
 }
 
 /*static*/ void
 VRDisplay::UpdateVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays, nsPIDOMWindowInner* aWindow)
 {
   nsTArray<RefPtr<VRDisplay>> displays;
 
   gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
--- a/dom/vr/VRDisplay.h
+++ b/dom/vr/VRDisplay.h
@@ -277,17 +277,17 @@ public:
   bool IsConnected() const;
 
   VRDisplayCapabilities* Capabilities();
   VRStageParameters* GetStageParameters();
 
   uint32_t DisplayId() const { return mDisplayId; }
   void GetDisplayName(nsAString& aDisplayName) const { aDisplayName = mDisplayName; }
 
-  static bool RefreshVRDisplays(dom::Navigator* aNavigator);
+  static bool RefreshVRDisplays(uint64_t aWindowId);
   static void UpdateVRDisplays(nsTArray<RefPtr<VRDisplay> >& aDisplays,
                                nsPIDOMWindowInner* aWindow);
 
   gfx::VRDisplayClient *GetClient() {
     return mClient;
   }
 
   virtual already_AddRefed<VREyeParameters> GetEyeParameters(VREye aEye);
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -264,23 +264,32 @@ VRManagerChild::UpdateDisplayInfo(nsTArr
 
   mDisplaysInitialized = true;
 }
 
 mozilla::ipc::IPCResult
 VRManagerChild::RecvUpdateDisplayInfo(nsTArray<VRDisplayInfo>&& aDisplayUpdates)
 {
   UpdateDisplayInfo(aDisplayUpdates);
-  for (auto& nav : mNavigatorCallbacks) {
+  for (auto& windowId : mNavigatorCallbacks) {
     /** We must call NotifyVRDisplaysUpdated for every
-     * Navigator in mNavigatorCallbacks to ensure that
+     * window's Navigator in mNavigatorCallbacks to ensure that
      * the promise returned by Navigator.GetVRDevices
      * can resolve.  This must happen even if no changes
      * to VRDisplays have been detected here.
      */
+    nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(windowId);
+    if (!window) {
+      continue;
+    }
+    ErrorResult result;
+    dom::Navigator* nav = window->GetNavigator(result);
+    if (NS_WARN_IF(result.Failed())) {
+      continue;
+    }
     nav->NotifyVRDisplaysUpdated();
   }
   mNavigatorCallbacks.Clear();
   return IPC_OK();
 }
 
 bool
 VRManagerChild::GetVRDisplays(nsTArray<RefPtr<VRDisplayClient>>& aDisplays)
@@ -295,21 +304,21 @@ VRManagerChild::GetVRDisplays(nsTArray<R
     Unused << SendGetDisplays(&displays);
     UpdateDisplayInfo(displays);
   }
   aDisplays = mDisplays;
   return true;
 }
 
 bool
-VRManagerChild::RefreshVRDisplaysWithCallback(dom::Navigator* aNavigator)
+VRManagerChild::RefreshVRDisplaysWithCallback(uint64_t aWindowId)
 {
   bool success = SendRefreshDisplays();
   if (success) {
-    mNavigatorCallbacks.AppendElement(aNavigator);
+    mNavigatorCallbacks.AppendElement(aWindowId);
   }
   return success;
 }
 
 int
 VRManagerChild::GetInputFrameID()
 {
   return mInputFrameID;
--- a/gfx/vr/ipc/VRManagerChild.h
+++ b/gfx/vr/ipc/VRManagerChild.h
@@ -44,17 +44,17 @@ public:
 
   // Indicate that an observer wants to receive VR events.
   void AddListener(dom::VREventObserver* aObserver);
   // Indicate that an observer should no longer receive VR events.
   void RemoveListener(dom::VREventObserver* aObserver);
 
   int GetInputFrameID();
   bool GetVRDisplays(nsTArray<RefPtr<VRDisplayClient> >& aDisplays);
-  bool RefreshVRDisplaysWithCallback(dom::Navigator* aNavigator);
+  bool RefreshVRDisplaysWithCallback(uint64_t aWindowId);
 
   static void InitSameProcess();
   static void InitWithGPUProcess(Endpoint<PVRManagerChild>&& aEndpoint);
   static bool InitForContent(Endpoint<PVRManagerChild>&& aEndpoint);
   static bool ReinitForContent(Endpoint<PVRManagerChild>&& aEndpoint);
   static void ShutDown();
 
   static bool IsCreated();
@@ -147,17 +147,17 @@ private:
   /**
   * Notify id of Texture When host side end its use. Transaction id is used to
   * make sure if there is no newer usage.
   */
   void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId);
 
   nsTArray<RefPtr<VRDisplayClient> > mDisplays;
   bool mDisplaysInitialized;
-  nsTArray<dom::Navigator*> mNavigatorCallbacks;
+  nsTArray<uint64_t> mNavigatorCallbacks;
   dom::GamepadManager* mGamepadManager;
 
   int32_t mInputFrameID;
 
   MessageLoop* mMessageLoop;
 
   struct FrameRequest;