diff options
author | Michael BrĂ¼ning <michael.bruning@qt.io> | 2020-01-15 16:48:35 +0100 |
---|---|---|
committer | Michael BrĂ¼ning <michael.bruning@qt.io> | 2020-01-16 13:10:15 +0000 |
commit | 8417e8352600298a901aeb0360eba25361b2343d (patch) | |
tree | c5a839ac489df2b9f954b72d4949776b9a254cfe | |
parent | fd8cf772447ea3567c4301cbd02ecdaeb312cd27 (diff) | |
download | qtwebengine-chromium-8417e8352600298a901aeb0360eba25361b2343d.tar.gz |
[Backport] Security bug 974375
Manual backport of patch:
Implement NativePixmapHandle validation
Now ClientNativePixmapDmaBuf and ScenicClientNativePixmapFactory
validate layout of the NativePixmapHandle to ensure that the buffer
fits the image.
Bug: 957314, 974375
Change-Id: Ifc0c0deae2c833e7a74ae96f84a41ae4a0657890
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
6 files changed, 71 insertions, 5 deletions
diff --git a/chromium/gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.cc b/chromium/gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.cc index 00f86254b99..d581fcff6f8 100644 --- a/chromium/gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.cc +++ b/chromium/gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.cc @@ -56,7 +56,8 @@ GpuMemoryBufferImplNativePixmap::CreateFromHandle( std::unique_ptr<gfx::ClientNativePixmap> native_pixmap = client_native_pixmap_factory->ImportFromHandle( CloneHandleForIPC(handle.native_pixmap_handle), size, format, usage); - DCHECK(native_pixmap); + if (!native_pixmap) + return nullptr; return base::WrapUnique(new GpuMemoryBufferImplNativePixmap( handle.id, size, format, std::move(callback), std::move(native_pixmap), diff --git a/chromium/ui/gfx/client_native_pixmap_factory.h b/chromium/ui/gfx/client_native_pixmap_factory.h index 566590a8f07..96aede0503c 100644 --- a/chromium/ui/gfx/client_native_pixmap_factory.h +++ b/chromium/ui/gfx/client_native_pixmap_factory.h @@ -26,7 +26,8 @@ class GFX_EXPORT ClientNativePixmapFactory { virtual ~ClientNativePixmapFactory() {} // Import the native pixmap from |handle| to be used in non-GPU processes. - // This function takes ownership of any file descriptors in |handle|. + // Implementations must verify that the buffer in |handle| fits an image of + // the specified |size| and |format|. Otherwise nullptr is returned. virtual std::unique_ptr<ClientNativePixmap> ImportFromHandle( gfx::NativePixmapHandle handle, const gfx::Size& size, diff --git a/chromium/ui/gfx/linux/client_native_pixmap_dmabuf.cc b/chromium/ui/gfx/linux/client_native_pixmap_dmabuf.cc index aab699da8ea..2c9e6ff9fa8 100644 --- a/chromium/ui/gfx/linux/client_native_pixmap_dmabuf.cc +++ b/chromium/ui/gfx/linux/client_native_pixmap_dmabuf.cc @@ -22,6 +22,7 @@ #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" +#include "ui/gfx/buffer_format_util.h" #include "ui/gfx/switches.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) @@ -169,11 +170,33 @@ bool ClientNativePixmapDmaBuf::IsConfigurationSupported( // static std::unique_ptr<gfx::ClientNativePixmap> ClientNativePixmapDmaBuf::ImportFromDmabuf(gfx::NativePixmapHandle handle, - const gfx::Size& size) { + const gfx::Size& size, + gfx::BufferFormat format) { std::array<PlaneInfo, kMaxPlanes> plane_info; + size_t expected_planes = gfx::NumberOfPlanesForLinearBufferFormat(format); + if (expected_planes == 0 || handle.planes.size() != expected_planes) { + return nullptr; + } + const size_t page_size = base::GetPageSize(); for (size_t i = 0; i < handle.planes.size(); ++i) { + // Verify that the plane buffer has appropriate size. + size_t min_stride = 0; + size_t subsample_factor = SubsamplingFactorForBufferFormat(format, i); + base::CheckedNumeric<size_t> plane_height = + (base::CheckedNumeric<size_t>(size.height()) + subsample_factor - 1) / + subsample_factor; + if (!gfx::RowSizeForBufferFormatChecked(size.width(), format, i, + &min_stride) || + handle.planes[i].stride < min_stride) { + return nullptr; + } + base::CheckedNumeric<size_t> min_size = + base::CheckedNumeric<size_t>(handle.planes[i].stride) * plane_height; + if (!min_size.IsValid() || handle.planes[i].size < min_size.ValueOrDie()) + return nullptr; + // mmap() fails if the offset argument is not page-aligned. // Since handle.planes[i].offset is possibly not page-aligned, we // have to map with an additional offset to be aligned to the page. diff --git a/chromium/ui/gfx/linux/client_native_pixmap_dmabuf.h b/chromium/ui/gfx/linux/client_native_pixmap_dmabuf.h index e337c574e28..6c5737cd3d2 100644 --- a/chromium/ui/gfx/linux/client_native_pixmap_dmabuf.h +++ b/chromium/ui/gfx/linux/client_native_pixmap_dmabuf.h @@ -27,7 +27,8 @@ class ClientNativePixmapDmaBuf : public gfx::ClientNativePixmap { static std::unique_ptr<gfx::ClientNativePixmap> ImportFromDmabuf( gfx::NativePixmapHandle handle, - const gfx::Size& size); + const gfx::Size& size, + gfx::BufferFormat format); ~ClientNativePixmapDmaBuf() override; diff --git a/chromium/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc b/chromium/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc index 652c23d86f1..201adb1dd3b 100644 --- a/chromium/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc +++ b/chromium/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc @@ -60,7 +60,7 @@ class ClientNativePixmapFactoryDmabuf : public ClientNativePixmapFactory { case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE: case gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE: return ClientNativePixmapDmaBuf::ImportFromDmabuf(std::move(handle), - size); + size, format); case gfx::BufferUsage::GPU_READ: case gfx::BufferUsage::SCANOUT: case gfx::BufferUsage::SCANOUT_VDA_WRITE: diff --git a/chromium/ui/ozone/platform/scenic/client_native_pixmap_factory_scenic.cc b/chromium/ui/ozone/platform/scenic/client_native_pixmap_factory_scenic.cc index 392670e097f..7df278b7110 100644 --- a/chromium/ui/ozone/platform/scenic/client_native_pixmap_factory_scenic.cc +++ b/chromium/ui/ozone/platform/scenic/client_native_pixmap_factory_scenic.cc @@ -10,6 +10,7 @@ #include "base/fuchsia/fuchsia_logging.h" #include "base/system/sys_info.h" +#include "ui/gfx/buffer_format_util.h" #include "ui/gfx/client_native_pixmap.h" #include "ui/gfx/client_native_pixmap_factory.h" #include "ui/gfx/native_pixmap_handle.h" @@ -101,8 +102,47 @@ class ScenicClientNativePixmapFactory : public gfx::ClientNativePixmapFactory { const gfx::Size& size, gfx::BufferFormat format, gfx::BufferUsage usage) override { + // |planes| may be empty for non-mappable pixmaps. No need to validate the + // handle in that case. if (handle.planes.empty()) + return std::make_unique<ClientNativePixmapFuchsia>(std::move(handle)); + + size_t expected_planes = gfx::NumberOfPlanesForLinearBufferFormat(format); + if (handle.planes.size() != expected_planes) + return nullptr; + + base::CheckedNumeric<size_t> vmo_size_checked = + base::CheckedNumeric<size_t>(handle.planes.back().offset) + + handle.planes.back().size; + if (!vmo_size_checked.IsValid()) { return nullptr; + } + size_t vmo_size = vmo_size_checked.ValueOrDie(); + + // Validate plane layout and buffer size. + for (size_t i = 0; i < handle.planes.size(); ++i) { + size_t min_stride = 0; + size_t subsample_factor = SubsamplingFactorForBufferFormat(format, i); + base::CheckedNumeric<size_t> plane_height = + (base::CheckedNumeric<size_t>(size.height()) + subsample_factor - 1) / + subsample_factor; + if (!gfx::RowSizeForBufferFormatChecked(size.width(), format, i, + &min_stride) || + handle.planes[i].stride < min_stride) { + return nullptr; + } + + base::CheckedNumeric<size_t> min_size = + base::CheckedNumeric<size_t>(handle.planes[i].stride) * plane_height; + if (!min_size.IsValid() || handle.planes[i].size < min_size.ValueOrDie()) + return nullptr; + + base::CheckedNumeric<size_t> end_pos = + base::CheckedNumeric<size_t>(handle.planes[i].offset) + + handle.planes[i].size; + if (!end_pos.IsValid() || end_pos.ValueOrDie() > vmo_size) + return nullptr; + } return std::make_unique<ClientNativePixmapFuchsia>(std::move(handle)); } |