Commit 337a57b9 authored by Ria Jiang's avatar Ria Jiang Committed by Commit Bot

Add a fuzzer for HitTestManager and HitTestAggregator.

Fuzz the hit-test data sent to HitTestManager and aggregate that in
HitTestAggregator. One problem the fuzzer revealed is fixed in
https://chromium-review.googlesource.com/c/chromium/src/+/957881.
Coverage report:
https://goto.google.com/riajiang-hit-test-fuzzer-report

Bug: 819435
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;master.tryserver.chromium.android:android_optional_gpu_tests_rel
Change-Id: I6029ffcaa0ccb696648d09da1e344aa2daa0eff6
Reviewed-on: https://chromium-review.googlesource.com/969927
Commit-Queue: Ria Jiang <riajiang@chromium.org>
Reviewed-by: default avatarRobert Kroeger <rjkroege@chromium.org>
Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@chromium.org>
Reviewed-by: default avatarKen Buchanan <kenrb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#546233}
parent bd53ee78
......@@ -5,6 +5,7 @@
import("//build/config/ui.gni")
import("//components/viz/viz.gni")
import("//gpu/vulkan/features.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
config("viz_service_implementation") {
}
......@@ -364,3 +365,18 @@ viz_source_set("perf_tests") {
"//testing/perf",
]
}
fuzzer_test("hit_test_manager_fuzzer") {
sources = [
"hit_test/hit_test_manager_fuzzer.cc",
]
libfuzzer_options = [ "max_len=81920" ]
deps = [
":service",
"//base/test:test_support",
"//components/viz/test:test_support",
"//mojo/edk",
]
}
......@@ -9,4 +9,9 @@ specific_include_rules = {
"+components/viz/host/host_frame_sink_manager.h",
"+components/viz/service/frame_sinks",
],
"hit_test_manager_fuzzer.cc": [
"+components/viz/service/frame_sinks",
"+components/viz/test",
"+mojo/edk/embedder",
],
}
......@@ -11,24 +11,20 @@
namespace viz {
namespace {
// TODO(gklassen): Review and select appropriate sizes based on
// telemetry / UMA.
constexpr uint32_t kInitialSize = 1024;
constexpr uint32_t kIncrementalSize = 1024;
constexpr uint32_t kMaxSize = 100 * 1024;
} // namespace
HitTestAggregator::HitTestAggregator(
const HitTestManager* hit_test_manager,
HitTestAggregatorDelegate* delegate,
LatestLocalSurfaceIdLookupDelegate* local_surface_id_lookup_delegate,
const FrameSinkId& frame_sink_id)
const FrameSinkId& frame_sink_id,
uint32_t initial_region_size,
uint32_t max_region_size)
: hit_test_manager_(hit_test_manager),
delegate_(delegate),
local_surface_id_lookup_delegate_(local_surface_id_lookup_delegate),
root_frame_sink_id_(frame_sink_id),
initial_region_size_(initial_region_size),
incremental_region_size_(initial_region_size),
max_region_size_(max_region_size),
weak_ptr_factory_(this) {
AllocateHitTestRegionArray();
}
......@@ -43,7 +39,7 @@ void HitTestAggregator::Aggregate(const SurfaceId& display_surface_id) {
}
void HitTestAggregator::GrowRegionList() {
ResizeHitTestRegionArray(write_size_ + kIncrementalSize);
ResizeHitTestRegionArray(write_size_ + incremental_region_size_);
}
void HitTestAggregator::Swap() {
......@@ -65,9 +61,9 @@ void HitTestAggregator::Swap() {
}
void HitTestAggregator::AllocateHitTestRegionArray() {
ResizeHitTestRegionArray(kInitialSize);
ResizeHitTestRegionArray(initial_region_size_);
SwapHandles();
ResizeHitTestRegionArray(kInitialSize);
ResizeHitTestRegionArray(initial_region_size_);
}
void HitTestAggregator::ResizeHitTestRegionArray(uint32_t size) {
......@@ -128,7 +124,7 @@ size_t HitTestAggregator::AppendRegion(size_t region_index,
const mojom::HitTestRegionPtr& region) {
size_t parent_index = region_index++;
if (region_index >= write_size_ - 1) {
if (write_size_ > kMaxSize) {
if (write_size_ > max_region_size_) {
MarkEndAt(parent_index);
return region_index;
} else {
......
......@@ -30,7 +30,9 @@ class VIZ_SERVICE_EXPORT HitTestAggregator {
const HitTestManager* hit_test_manager,
HitTestAggregatorDelegate* delegate,
LatestLocalSurfaceIdLookupDelegate* local_surface_id_lookup_delegate,
const FrameSinkId& frame_sink_id);
const FrameSinkId& frame_sink_id,
uint32_t initial_region_size = 1024,
uint32_t max_region_size = 100 * 1024);
~HitTestAggregator();
// Called after surfaces have been aggregated into the DisplayFrame.
......@@ -101,6 +103,13 @@ class VIZ_SERVICE_EXPORT HitTestAggregator {
// This is the FrameSinkId for the corresponding root CompositorFrameSink.
const FrameSinkId root_frame_sink_id_;
// Initial hit-test region size.
// TODO(https://crbug.com/746385): Review and select appropriate sizes based
// on telemetry / UMA.
const uint32_t initial_region_size_;
const uint32_t incremental_region_size_;
const uint32_t max_region_size_;
// This is the set of FrameSinkIds referenced in the aggregation so far, used
// to detect cycles.
base::flat_set<FrameSinkId> referenced_child_regions_;
......
// Copyright 2018 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.
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "base/command_line.h"
#include "base/test/fuzzed_data_provider.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "components/viz/service/hit_test/hit_test_aggregator.h"
#include "components/viz/service/hit_test/hit_test_manager.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "components/viz/test/test_latest_local_surface_id_lookup_delegate.h"
#include "mojo/edk/embedder/embedder.h"
namespace {
// TODO(riajiang): Move into common functions that can be used by the fuzzer
// for HitTestQuery.
uint32_t GetNextUInt32NonZero(base::FuzzedDataProvider* fuzz) {
return fuzz->ConsumeUint32InRange(1, std::numeric_limits<uint32_t>::max());
}
gfx::Transform GetNextTransform(base::FuzzedDataProvider* fuzz) {
gfx::Transform transform;
if (fuzz->ConsumeBool() && fuzz->remaining_bytes() >= sizeof(transform)) {
std::string matrix_bytes = fuzz->ConsumeBytes(sizeof(gfx::Transform));
memcpy(&transform, matrix_bytes.data(), sizeof(gfx::Transform));
}
return transform;
}
void SubmitHitTestRegionList(
base::FuzzedDataProvider* fuzz,
viz::TestLatestLocalSurfaceIdLookupDelegate* delegate,
viz::FrameSinkManagerImpl* frame_sink_manager,
const viz::SurfaceId& surface_id,
bool support_is_root);
void AddHitTestRegion(base::FuzzedDataProvider* fuzz,
std::vector<viz::mojom::HitTestRegionPtr>* regions,
uint32_t child_count,
viz::TestLatestLocalSurfaceIdLookupDelegate* delegate,
viz::FrameSinkManagerImpl* frame_sink_manager,
const viz::SurfaceId& surface_id) {
if (!child_count)
return;
// If there's not enough space left for a HitTestRegion, then skip.
if (fuzz->remaining_bytes() < sizeof(viz::mojom::HitTestRegion))
return;
auto hit_test_region = viz::mojom::HitTestRegion::New();
hit_test_region->flags = fuzz->ConsumeUint16();
if (fuzz->ConsumeBool())
hit_test_region->flags |= viz::mojom::kHitTestChildSurface;
hit_test_region->frame_sink_id =
viz::FrameSinkId(fuzz->ConsumeUint8(), fuzz->ConsumeUint8());
hit_test_region->rect =
gfx::Rect(fuzz->ConsumeUint8(), fuzz->ConsumeUint8(),
fuzz->ConsumeUint16(), fuzz->ConsumeUint16());
hit_test_region->transform = GetNextTransform(fuzz);
if (fuzz->ConsumeBool() &&
(hit_test_region->flags & viz::mojom::kHitTestChildSurface)) {
// If there's not enough space left for a LocalSurfaceId, then skip.
if (fuzz->remaining_bytes() < sizeof(viz::LocalSurfaceId))
return;
uint32_t last_frame_sink_id_client_id =
surface_id.frame_sink_id().client_id();
uint32_t last_frame_sink_id_sink_id = surface_id.frame_sink_id().sink_id();
viz::FrameSinkId frame_sink_id(last_frame_sink_id_client_id + 1,
last_frame_sink_id_sink_id + 1);
viz::LocalSurfaceId local_surface_id(GetNextUInt32NonZero(fuzz),
GetNextUInt32NonZero(fuzz),
base::UnguessableToken::Create());
SubmitHitTestRegionList(fuzz, delegate, frame_sink_manager,
viz::SurfaceId(frame_sink_id, local_surface_id),
false /* support_is_root */);
}
regions->push_back(std::move(hit_test_region));
AddHitTestRegion(fuzz, regions, child_count - 1, delegate, frame_sink_manager,
surface_id);
}
void SubmitHitTestRegionList(
base::FuzzedDataProvider* fuzz,
viz::TestLatestLocalSurfaceIdLookupDelegate* delegate,
viz::FrameSinkManagerImpl* frame_sink_manager,
const viz::SurfaceId& surface_id,
bool support_is_root) {
// If there's not enough space left for a HitTestRegionList, then skip.
if (fuzz->remaining_bytes() < sizeof(viz::mojom::HitTestRegionList))
return;
auto hit_test_region_list = viz::mojom::HitTestRegionList::New();
hit_test_region_list->flags = fuzz->ConsumeUint16();
if (fuzz->ConsumeBool())
hit_test_region_list->flags |= viz::mojom::kHitTestChildSurface;
hit_test_region_list->bounds =
gfx::Rect(fuzz->ConsumeUint8(), fuzz->ConsumeUint8(),
fuzz->ConsumeUint16(), fuzz->ConsumeUint16());
hit_test_region_list->transform = GetNextTransform(fuzz);
uint32_t child_count = fuzz->ConsumeUint16();
AddHitTestRegion(fuzz, &hit_test_region_list->regions, child_count, delegate,
frame_sink_manager, surface_id);
delegate->SetSurfaceIdMap(surface_id);
viz::CompositorFrameSinkSupport support(
nullptr /* client */, frame_sink_manager, surface_id.frame_sink_id(),
support_is_root, false /* needs_sync_points */);
support.SubmitCompositorFrame(
surface_id.local_surface_id(), viz::MakeDefaultCompositorFrame(),
fuzz->ConsumeBool() ? std::move(hit_test_region_list) : nullptr);
}
class Environment {
public:
Environment() {
// Initialize environment so that we can create the mojo shared memory
// handles.
base::CommandLine::Init(0, nullptr);
mojo::edk::Init();
}
};
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t num_bytes) {
// Initialize the environment only once.
static Environment environment;
base::FuzzedDataProvider fuzz(data, num_bytes);
viz::FrameSinkManagerImpl frame_sink_manager;
viz::TestLatestLocalSurfaceIdLookupDelegate delegate;
viz::TestLatestLocalSurfaceIdLookupDelegate* lsi_delegate =
fuzz.ConsumeBool() ? &delegate : nullptr;
// If there's not enough space left for a LocalSurfaceId, then skip.
if (fuzz.remaining_bytes() < sizeof(viz::LocalSurfaceId))
return 0;
constexpr uint32_t root_client_id = 1;
constexpr uint32_t root_sink_id = 1;
viz::FrameSinkId frame_sink_id(root_client_id, root_sink_id);
viz::LocalSurfaceId local_surface_id(GetNextUInt32NonZero(&fuzz),
GetNextUInt32NonZero(&fuzz),
base::UnguessableToken::Create());
viz::SurfaceId surface_id(frame_sink_id, local_surface_id);
viz::HitTestAggregator aggregator(
frame_sink_manager.hit_test_manager(), &frame_sink_manager, lsi_delegate,
frame_sink_id, 10 /* initial_region_size */, 100 /* max_region_size */);
SubmitHitTestRegionList(&fuzz, &delegate, &frame_sink_manager, surface_id,
true /* support_is_root */);
viz::SurfaceId aggregate_surface_id = surface_id;
if (fuzz.ConsumeBool() && fuzz.remaining_bytes() >= sizeof(viz::SurfaceId)) {
viz::FrameSinkId frame_sink_id(GetNextUInt32NonZero(&fuzz),
GetNextUInt32NonZero(&fuzz));
viz::LocalSurfaceId local_surface_id(GetNextUInt32NonZero(&fuzz),
GetNextUInt32NonZero(&fuzz),
base::UnguessableToken::Create());
aggregate_surface_id = viz::SurfaceId(frame_sink_id, local_surface_id);
}
aggregator.Aggregate(aggregate_surface_id);
viz::Surface* surface = frame_sink_manager.surface_manager()->GetSurfaceForId(
aggregate_surface_id);
if (surface)
frame_sink_manager.surface_manager()->SurfaceDiscarded(surface);
return 0;
}
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