Commit 4ef26c53 authored by Alexander Dunaev's avatar Alexander Dunaev Committed by Commit Bot

[ozone/wayland] The fuzzer for wayland buffers is introduced.

Two issues have been discovered and resolved:
- The client is now destroyed properly when the test server thread is stopped, so the socket descriptors are freed.
- The buffer manager now handles the passed file properly so the file descriptor is freed.

R=msisov@igalia.com

Bug: 873132
Change-Id: I8aabc38da915b61987a995a94fe7c343ce982f06
Reviewed-on: https://chromium-review.googlesource.com/c/1454530
Commit-Queue: Alexander Dunaev <adunaev@igalia.com>
Reviewed-by: default avatarMaksim Sisov <msisov@igalia.com>
Reviewed-by: default avatarRobert Kroeger <rjkroege@chromium.org>
Cr-Commit-Position: refs/heads/master@{#629586}
parent 591488d3
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
visibility = [ "//ui/ozone/*" ] visibility = [ "//ui/ozone/*" ]
import("//build/config/linux/pkg_config.gni") import("//build/config/linux/pkg_config.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
import("//ui/ozone/platform/wayland/wayland.gni") import("//ui/ozone/platform/wayland/wayland.gni")
pkg_config("wayland-egl") { pkg_config("wayland-egl") {
...@@ -206,6 +207,10 @@ source_set("test_support") { ...@@ -206,6 +207,10 @@ source_set("test_support") {
deps = [ deps = [
"//base:base", "//base:base",
"//ui/ozone:platform",
]
public_deps = [
"//skia", "//skia",
"//testing/gmock", "//testing/gmock",
"//third_party/wayland:wayland_server", "//third_party/wayland:wayland_server",
...@@ -262,3 +267,18 @@ source_set("wayland_unittests") { ...@@ -262,3 +267,18 @@ source_set("wayland_unittests") {
defines += [ "WAYLAND_GBM" ] defines += [ "WAYLAND_GBM" ]
} }
} }
fuzzer_test("wayland_buffer_fuzzer") {
defines = [ "WL_HIDE_DEPRECATED" ]
sources = [
"wayland_buffer_fuzzer.cc",
]
deps = [
":test_support",
":wayland",
"//base/test:test_support",
"//build/config/linux/libdrm",
"//ui/gfx:test_support",
"//ui/platform_window:platform_window",
]
}
...@@ -11,11 +11,6 @@ const struct wl_buffer_interface kMockWlBufferImpl = {&DestroyResource}; ...@@ -11,11 +11,6 @@ const struct wl_buffer_interface kMockWlBufferImpl = {&DestroyResource};
MockBuffer::MockBuffer(wl_resource* resource, std::vector<base::ScopedFD>&& fds) MockBuffer::MockBuffer(wl_resource* resource, std::vector<base::ScopedFD>&& fds)
: ServerObject(resource), fds_(std::move(fds)) {} : ServerObject(resource), fds_(std::move(fds)) {}
MockBuffer::~MockBuffer() { MockBuffer::~MockBuffer() {}
for (auto& fd : fds_) {
LOG(WARNING) << "Will close FD: " << fd.get();
fd.reset();
}
}
} // namespace wl } // namespace wl
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "ui/ozone/platform/wayland/test/server_object.h" #include "ui/ozone/platform/wayland/test/server_object.h"
struct wl_client;
struct wl_resource; struct wl_resource;
namespace wl { namespace wl {
...@@ -25,8 +24,6 @@ class MockBuffer : public ServerObject { ...@@ -25,8 +24,6 @@ class MockBuffer : public ServerObject {
MockBuffer(wl_resource* resource, std::vector<base::ScopedFD>&& fds); MockBuffer(wl_resource* resource, std::vector<base::ScopedFD>&& fds);
~MockBuffer() override; ~MockBuffer() override;
MOCK_METHOD2(Destroy, void(wl_client* client, wl_resource* resource));
private: private:
std::vector<base::ScopedFD> fds_; std::vector<base::ScopedFD> fds_;
......
...@@ -34,9 +34,10 @@ void Create(wl_client* client, ...@@ -34,9 +34,10 @@ void Create(wl_client* client,
auto* buffer_params = auto* buffer_params =
GetUserDataAs<MockZwpLinuxBufferParamsV1>(buffer_params_resource); GetUserDataAs<MockZwpLinuxBufferParamsV1>(buffer_params_resource);
wl_resource* buffer_resource = CreateResourceWithImpl<MockBuffer>( wl_resource* buffer_resource =
client, &wl_buffer_interface, 1, &kMockWlBufferImpl, 0, CreateResourceWithImpl<::testing::NiceMock<MockBuffer>>(
std::move(buffer_params->fds_)); client, &wl_buffer_interface, 1, &kMockWlBufferImpl, 0,
std::move(buffer_params->fds_));
zwp_linux_buffer_params_v1_send_created(buffer_params_resource, zwp_linux_buffer_params_v1_send_created(buffer_params_resource,
buffer_resource); buffer_resource);
......
...@@ -17,7 +17,7 @@ namespace { ...@@ -17,7 +17,7 @@ namespace {
constexpr uint32_t kLinuxDmabufVersion = 1; constexpr uint32_t kLinuxDmabufVersion = 1;
void CreateParams(wl_client* client, wl_resource* resource, uint32_t id) { void CreateParams(wl_client* client, wl_resource* resource, uint32_t id) {
CreateResourceWithImpl<MockZwpLinuxBufferParamsV1>( CreateResourceWithImpl<::testing::NiceMock<MockZwpLinuxBufferParamsV1>>(
client, &zwp_linux_buffer_params_v1_interface, client, &zwp_linux_buffer_params_v1_interface,
wl_resource_get_version(resource), &kMockZwpLinuxBufferParamsV1Impl, id); wl_resource_get_version(resource), &kMockZwpLinuxBufferParamsV1Impl, id);
......
...@@ -20,9 +20,10 @@ constexpr uint32_t kCompositorVersion = 4; ...@@ -20,9 +20,10 @@ constexpr uint32_t kCompositorVersion = 4;
void CreateSurface(wl_client* client, void CreateSurface(wl_client* client,
wl_resource* compositor_resource, wl_resource* compositor_resource,
uint32_t id) { uint32_t id) {
wl_resource* resource = CreateResourceWithImpl<MockSurface>( wl_resource* resource =
client, &wl_surface_interface, CreateResourceWithImpl<::testing::NiceMock<MockSurface>>(
wl_resource_get_version(compositor_resource), &kMockSurfaceImpl, id); client, &wl_surface_interface,
wl_resource_get_version(compositor_resource), &kMockSurfaceImpl, id);
GetUserDataAs<TestCompositor>(compositor_resource) GetUserDataAs<TestCompositor>(compositor_resource)
->AddSurface(GetUserDataAs<MockSurface>(resource)); ->AddSurface(GetUserDataAs<MockSurface>(resource));
} }
......
...@@ -30,6 +30,9 @@ TestWaylandServerThread::TestWaylandServerThread() ...@@ -30,6 +30,9 @@ TestWaylandServerThread::TestWaylandServerThread()
controller_(FROM_HERE) {} controller_(FROM_HERE) {}
TestWaylandServerThread::~TestWaylandServerThread() { TestWaylandServerThread::~TestWaylandServerThread() {
if (client_)
wl_client_destroy(client_);
Resume(); Resume();
Stop(); Stop();
} }
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This fuzzer tests browser-side implementation of
// ozone::mojom::WaylandConnection.
#include <drm_fourcc.h>
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/message_loop/message_loop.h"
#include "base/test/fuzzed_data_provider.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/wayland_connection.h"
#include "ui/ozone/platform/wayland/wayland_window.h"
#include "ui/platform_window/platform_window_delegate.h"
#include "ui/platform_window/platform_window_init_properties.h"
using testing::_;
namespace {
// Off-class equivalent of WaylandTest::Sync.
void Sync(wl::TestWaylandServerThread* server) {
DCHECK(server);
server->Resume();
base::RunLoop().RunUntilIdle();
server->Pause();
}
// Copied from ui/ozone/test/mock_platform_window_delegate.h to avoid
// dependency from the whole library (it causes link problems).
class MockPlatformWindowDelegate : public ui::PlatformWindowDelegate {
public:
MockPlatformWindowDelegate() = default;
~MockPlatformWindowDelegate() = default;
MOCK_METHOD1(OnBoundsChanged, void(const gfx::Rect& new_bounds));
MOCK_METHOD1(OnDamageRect, void(const gfx::Rect& damaged_region));
MOCK_METHOD1(DispatchEvent, void(ui::Event* event));
MOCK_METHOD0(OnCloseRequest, void());
MOCK_METHOD0(OnClosed, void());
MOCK_METHOD1(OnWindowStateChanged, void(ui::PlatformWindowState new_state));
MOCK_METHOD0(OnLostCapture, void());
MOCK_METHOD1(OnAcceleratedWidgetAvailable,
void(gfx::AcceleratedWidget widget));
MOCK_METHOD0(OnAcceleratedWidgetDestroyed, void());
MOCK_METHOD1(OnActivationChanged, void(bool active));
private:
DISALLOW_COPY_AND_ASSIGN(MockPlatformWindowDelegate);
};
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
base::FuzzedDataProvider data_provider(data, size);
std::vector<uint32_t> known_fourccs{
DRM_FORMAT_R8, DRM_FORMAT_GR88, DRM_FORMAT_ABGR8888,
DRM_FORMAT_XBGR8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888,
DRM_FORMAT_XRGB2101010, DRM_FORMAT_XBGR2101010, DRM_FORMAT_RGB565,
DRM_FORMAT_UYVY, DRM_FORMAT_NV12, DRM_FORMAT_YVU420};
base::MessageLoopForUI message_loop;
MockPlatformWindowDelegate delegate;
std::unique_ptr<ui::WaylandConnection> connection =
std::make_unique<ui::WaylandConnection>();
std::unique_ptr<ui::WaylandWindow> window =
std::make_unique<ui::WaylandWindow>(&delegate, connection.get());
gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget;
wl::TestWaylandServerThread server;
CHECK(server.Start(6));
CHECK(connection->Initialize());
EXPECT_CALL(delegate, OnAcceleratedWidgetAvailable(_))
.WillOnce(testing::SaveArg<0>(&widget));
ui::PlatformWindowInitProperties properties;
properties.bounds = gfx::Rect(0, 0, 800, 600);
properties.type = ui::PlatformWindowType::kWindow;
CHECK(window->Initialize(std::move(properties)));
CHECK_NE(widget, gfx::kNullAcceleratedWidget);
base::RunLoop().RunUntilIdle();
server.Pause();
base::FilePath temp_path;
EXPECT_TRUE(base::CreateTemporaryFile(&temp_path));
base::File temp(temp_path,
base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS);
// 10K screens are reality these days.
const uint32_t kWidth = data_provider.ConsumeIntegralInRange(1U, 20000U);
const uint32_t kHeight = data_provider.ConsumeIntegralInRange(1U, 20000U);
// The buffer manager opens a file descriptor for each plane so |plane_count|
// cannot be really large. Technically, the maximum is |ulimit| minus number
// of file descriptors opened by this process already (which is 17 at the time
// of writing) but there is little sense in having more than a few planes in a
// real system so here is a hard limit of 500.
const uint32_t kPlaneCount = data_provider.ConsumeIntegralInRange(1U, 500U);
const uint32_t kFormat = known_fourccs[data_provider.ConsumeIntegralInRange(
0UL, known_fourccs.size() - 1)];
std::vector<uint32_t> strides(kPlaneCount);
std::vector<uint32_t> offsets(kPlaneCount);
std::vector<uint64_t> modifiers(kPlaneCount);
for (uint32_t i = 0; i < kPlaneCount; ++i) {
strides[i] = data_provider.ConsumeIntegralInRange(1U, UINT_MAX);
offsets[i] = data_provider.ConsumeIntegralInRange(0U, UINT_MAX);
modifiers[i] =
data_provider.ConsumeIntegralInRange(uint64_t(0), UINT64_MAX);
}
const uint32_t kBufferId = 1;
EXPECT_CALL(*server.zwp_linux_dmabuf_v1(), CreateParams(_, _, _));
connection->CreateZwpLinuxDmabuf(std::move(temp), kWidth, kHeight, strides,
offsets, kFormat, modifiers, kPlaneCount,
kBufferId);
Sync(&server);
Sync(&server);
connection->DestroyZwpLinuxDmabuf(kBufferId);
Sync(&server);
return 0;
}
...@@ -54,7 +54,8 @@ WaylandBufferManager::WaylandBufferManager( ...@@ -54,7 +54,8 @@ WaylandBufferManager::WaylandBufferManager(
WaylandConnection* connection) WaylandConnection* connection)
: zwp_linux_dmabuf_(zwp_linux_dmabuf), connection_(connection) { : zwp_linux_dmabuf_(zwp_linux_dmabuf), connection_(connection) {
static const zwp_linux_dmabuf_v1_listener dmabuf_listener = { static const zwp_linux_dmabuf_v1_listener dmabuf_listener = {
&WaylandBufferManager::Format, &WaylandBufferManager::Modifiers, &WaylandBufferManager::Format,
&WaylandBufferManager::Modifiers,
}; };
zwp_linux_dmabuf_v1_add_listener(zwp_linux_dmabuf_.get(), &dmabuf_listener, zwp_linux_dmabuf_v1_add_listener(zwp_linux_dmabuf_.get(), &dmabuf_listener,
this); this);
...@@ -87,23 +88,22 @@ bool WaylandBufferManager::CreateBuffer(base::File file, ...@@ -87,23 +88,22 @@ bool WaylandBufferManager::CreateBuffer(base::File file,
modifiers, planes_count, buffer_id)) { modifiers, planes_count, buffer_id)) {
// base::File::Close() has an assertion that checks if blocking operations // base::File::Close() has an assertion that checks if blocking operations
// are allowed. Thus, manually close the fd here. // are allowed. Thus, manually close the fd here.
base::ScopedFD fd(file.TakePlatformFile()); base::ScopedFD deleter(file.TakePlatformFile());
fd.reset();
return false; return false;
} }
base::ScopedFD fd(file.TakePlatformFile());
// Store |params| connected to |buffer_id| to track buffer creation and // Store |params| connected to |buffer_id| to track buffer creation and
// identify, which buffer a client wants to use. // identify, which buffer a client wants to use.
DCHECK(zwp_linux_dmabuf_); DCHECK(zwp_linux_dmabuf_);
struct zwp_linux_buffer_params_v1* params = struct zwp_linux_buffer_params_v1* params =
zwp_linux_dmabuf_v1_create_params(zwp_linux_dmabuf_.get()); zwp_linux_dmabuf_v1_create_params(zwp_linux_dmabuf_.get());
std::unique_ptr<Buffer> buffer =
std::make_unique<Buffer>(buffer_id, params, gfx::Size(width, height));
buffers_.insert(std::pair<uint32_t, std::unique_ptr<Buffer>>( buffers_.insert(std::pair<uint32_t, std::unique_ptr<Buffer>>(
buffer_id, std::move(buffer))); buffer_id,
std::make_unique<Buffer>(buffer_id, params, gfx::Size(width, height))));
uint32_t fd = file.TakePlatformFile();
for (size_t i = 0; i < planes_count; i++) { for (size_t i = 0; i < planes_count; i++) {
uint32_t modifier_lo = 0; uint32_t modifier_lo = 0;
uint32_t modifier_hi = 0; uint32_t modifier_hi = 0;
...@@ -114,8 +114,9 @@ bool WaylandBufferManager::CreateBuffer(base::File file, ...@@ -114,8 +114,9 @@ bool WaylandBufferManager::CreateBuffer(base::File file,
DCHECK_EQ(planes_count, 1u) << "Invalid modifier may be passed only in " DCHECK_EQ(planes_count, 1u) << "Invalid modifier may be passed only in "
"case of single plane format being used"; "case of single plane format being used";
} }
zwp_linux_buffer_params_v1_add(params, fd, i /* plane id */, offsets[i], zwp_linux_buffer_params_v1_add(params, fd.get(), i /* plane id */,
strides[i], modifier_hi, modifier_lo); offsets[i], strides[i], modifier_hi,
modifier_lo);
} }
zwp_linux_buffer_params_v1_add_listener(params, &params_listener, this); zwp_linux_buffer_params_v1_add_listener(params, &params_listener, this);
zwp_linux_buffer_params_v1_create(params, width, height, format, 0); zwp_linux_buffer_params_v1_create(params, width, height, format, 0);
......
...@@ -52,7 +52,8 @@ WaylandConnection::~WaylandConnection() = default; ...@@ -52,7 +52,8 @@ WaylandConnection::~WaylandConnection() = default;
bool WaylandConnection::Initialize() { bool WaylandConnection::Initialize() {
static const wl_registry_listener registry_listener = { static const wl_registry_listener registry_listener = {
&WaylandConnection::Global, &WaylandConnection::GlobalRemove, &WaylandConnection::Global,
&WaylandConnection::GlobalRemove,
}; };
display_.reset(wl_display_connect(nullptr)); display_.reset(wl_display_connect(nullptr));
...@@ -364,7 +365,8 @@ void WaylandConnection::Global(void* data, ...@@ -364,7 +365,8 @@ void WaylandConnection::Global(void* data,
const char* interface, const char* interface,
uint32_t version) { uint32_t version) {
static const wl_seat_listener seat_listener = { static const wl_seat_listener seat_listener = {
&WaylandConnection::Capabilities, &WaylandConnection::Name, &WaylandConnection::Capabilities,
&WaylandConnection::Name,
}; };
static const xdg_shell_listener shell_listener = { static const xdg_shell_listener shell_listener = {
&WaylandConnection::Ping, &WaylandConnection::Ping,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment