Commit 36a33a61 authored by Clovis PJ's avatar Clovis PJ Committed by Commit Bot

Adding Presentation Service MojoLPM fuzzer.

Bug: 1113721
Change-Id: I79132bdf3ea1f276994932e8e6a882c60d8ea946
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2364618Reviewed-by: default avatarOliver Chang <ochang@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Commit-Queue: Clovis PJ <clovispj@google.com>
Cr-Commit-Position: refs/heads/master@{#808256}
parent 19a94963
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
# Fuzzers for content/ components. # Fuzzers for content/ components.
import("//mojo/public/tools/bindings/mojom.gni")
import("//mojo/public/tools/fuzzers/mojolpm.gni") import("//mojo/public/tools/fuzzers/mojolpm.gni")
import("//testing/libfuzzer/fuzzer_test.gni") import("//testing/libfuzzer/fuzzer_test.gni")
import("//third_party/protobuf/proto_library.gni") import("//third_party/protobuf/proto_library.gni")
...@@ -281,3 +282,72 @@ if (is_linux) { ...@@ -281,3 +282,72 @@ if (is_linux) {
proto_deps = [ "//media/capture/mojom:video_capture_mojolpm" ] proto_deps = [ "//media/capture/mojom:video_capture_mojolpm" ]
} }
} }
mojolpm_fuzzer_test("presentation_service_mojolpm_fuzzer") {
sources = [ "presentation_service_mojolpm_fuzzer.cc" ]
proto_source = "presentation_service_mojolpm_fuzzer.proto"
deps = [
":controller_presentation_service_delegate_for_fuzzing",
"//base/test:test_support",
"//content/browser:for_content_tests",
"//content/public/browser:browser",
"//content/test:content_unittests",
"//content/test:test_support",
"//services/network:test_support",
"//storage/browser:test_support",
"//ui/events/devices:devices",
]
proto_deps = [
":controller_presentation_service_delegate_for_fuzzing_proto",
"//third_party/blink/public/mojom:mojom_platform_mojolpm",
]
}
if (enable_mojom_fuzzer) {
static_library("controller_presentation_service_delegate_for_fuzzing") {
# Should only be used in the fuzzer this was made for
visibility = [ ":presentation_service_mojolpm_fuzzer" ]
sources = [
"controller_presentation_service_delegate_for_fuzzing.cc",
"controller_presentation_service_delegate_for_fuzzing.h",
]
deps = [
":controller_presentation_service_delegate_for_fuzzing_proto",
"//base",
"//content/public/browser:browser",
"//media:test_support",
"//third_party/blink/public/mojom:mojom_platform_mojolpm",
"//url/mojom:url_mojom_gurl_mojolpm",
]
testonly = true
}
proto_library("controller_presentation_service_delegate_for_fuzzing_proto") {
# Should only be used in the fuzzer this was made for, and its
# generated rules
visibility = [ "//content/test/fuzzer:*" ]
proto_in_dir = "//"
sources = [ "controller_presentation_service_delegate_for_fuzzing.proto" ]
generate_python = false
proto_deps = [
"//third_party/blink/public/mojom:mojom_platform_mojolpm",
"//url/mojom:url_mojom_gurl_mojolpm",
]
link_deps = [
"//third_party/blink/public/mojom:mojom_platform_mojolpm",
"//url/mojom:url_mojom_gurl_mojolpm",
]
import_dirs = [ root_gen_dir ]
testonly = true
}
}
// Copyright 2020 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 <memory>
#include <string>
#include <utility>
#include "content/test/fuzzer/controller_presentation_service_delegate_for_fuzzing.h"
#include "base/notreached.h"
#include "base/task/post_task.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/blink/public/mojom/presentation/presentation.mojom-mojolpm.h"
#include "url/mojom/url.mojom-mojolpm.h"
// Code copy of a limit of the same name
// This allows preventing the fake making bad calls
// See "content/browser/presentation/presentation_service_impl.cc"
static constexpr size_t kMaxPresentationIdLength = 256;
ControllerPresentationServiceDelegateForFuzzing::
ControllerPresentationServiceDelegateForFuzzing() {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
ControllerPresentationServiceDelegateForFuzzing::
~ControllerPresentationServiceDelegateForFuzzing() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
for (auto& observer_pair : observers_)
observer_pair.second->OnDelegateDestroyed();
}
void ControllerPresentationServiceDelegateForFuzzing::AddObserver(
int render_process_id,
int render_frame_id,
ControllerPresentationServiceDelegate::Observer* observer) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
observers_[content::GlobalFrameRoutingId(render_process_id,
render_frame_id)] = observer;
}
void ControllerPresentationServiceDelegateForFuzzing::RemoveObserver(
int render_process_id,
int render_frame_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
observers_.erase(
content::GlobalFrameRoutingId(render_process_id, render_frame_id));
}
void ControllerPresentationServiceDelegateForFuzzing::Reset(
int render_process_id,
int render_frame_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Intentionally, we do not reset all callbacks, in order to mimic
// `ControllerPresentationServiceDelegateImpl` as closely as possible.
listeners_.clear();
set_default_presentation_urls_callback_.Reset();
}
void ControllerPresentationServiceDelegateForFuzzing::
SetDefaultPresentationUrls(
const content::PresentationRequest& request,
content::DefaultPresentationConnectionCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
set_default_presentation_urls_callback_ = std::move(callback);
}
void ControllerPresentationServiceDelegateForFuzzing::StartPresentation(
const content::PresentationRequest& request,
content::PresentationConnectionCallback success_cb,
content::PresentationConnectionErrorCallback error_cb) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
start_presentation_success_cb_ = std::move(success_cb);
start_presentation_error_cb_ = std::move(error_cb);
}
void ControllerPresentationServiceDelegateForFuzzing::ReconnectPresentation(
const content::PresentationRequest& request,
const std::string& presentation_id,
content::PresentationConnectionCallback success_cb,
content::PresentationConnectionErrorCallback error_cb) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
reconnect_presentation_success_cb_ = std::move(success_cb);
reconnect_presentation_error_cb_ = std::move(error_cb);
}
void ControllerPresentationServiceDelegateForFuzzing::CloseConnection(
int render_process_id,
int render_frame_id,
const std::string& presentation_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
start_presentation_success_cb_.Reset();
start_presentation_error_cb_.Reset();
reconnect_presentation_success_cb_.Reset();
reconnect_presentation_error_cb_.Reset();
}
void ControllerPresentationServiceDelegateForFuzzing::Terminate(
int render_process_id,
int render_frame_id,
const std::string& presentation_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
start_presentation_success_cb_.Reset();
start_presentation_error_cb_.Reset();
reconnect_presentation_success_cb_.Reset();
reconnect_presentation_error_cb_.Reset();
}
std::unique_ptr<media::FlingingController>
ControllerPresentationServiceDelegateForFuzzing::GetFlingingController(
int render_process_id,
int render_frame_id,
const std::string& presentation_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// This should not fail. As the MojoLPM fuzzer this fake was designed to work
// with has no way to call this.
NOTIMPLEMENTED();
return nullptr;
}
void ControllerPresentationServiceDelegateForFuzzing::
ListenForConnectionStateChange(
int render_process_id,
int render_frame_id,
const blink::mojom::PresentationInfo& connection,
const content::PresentationConnectionStateChangedCallback&
state_changed_cb) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
listen_for_connection_state_change_state_changed_cb_ =
std::move(state_changed_cb);
}
bool ControllerPresentationServiceDelegateForFuzzing::
AddScreenAvailabilityListener(
int render_process_id,
int render_frame_id,
content::PresentationScreenAvailabilityListener* listener) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
listeners_[listener->GetAvailabilityUrl()] = listener;
return true;
}
void ControllerPresentationServiceDelegateForFuzzing::
RemoveScreenAvailabilityListener(
int render_process_id,
int render_frame_id,
content::PresentationScreenAvailabilityListener* listener) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
listeners_.erase(listener->GetAvailabilityUrl());
}
void ControllerPresentationServiceDelegateForFuzzing::
HandleListenersGetAvailabilityUrl(
const mojolpm::url::mojom::Url& proto_url) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
GURL url;
if (!mojolpm::FromProto(proto_url, url))
return;
base::PostTask(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing::
CallListenersGetAvailabilityUrl,
GetWeakPtr(), std::move(url)));
}
void ControllerPresentationServiceDelegateForFuzzing::
CallListenersGetAvailabilityUrl(GURL url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!listeners_.count(url))
return;
listeners_[url]->GetAvailabilityUrl();
}
void ControllerPresentationServiceDelegateForFuzzing::
HandleListenersOnScreenAvailabilityChanged(
const mojolpm::url::mojom::Url& proto_url,
const mojolpm::blink::mojom::ScreenAvailability&
proto_screen_availability) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
GURL url;
if (!mojolpm::FromProto(proto_url, url))
return;
blink::mojom::ScreenAvailability screen_availability;
if (!mojolpm::FromProto(proto_screen_availability, screen_availability))
return;
base::PostTask(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing::
CallListenersOnScreenAvailabilityChanged,
GetWeakPtr(), std::move(url),
std::move(screen_availability)));
}
void ControllerPresentationServiceDelegateForFuzzing::
CallListenersOnScreenAvailabilityChanged(
GURL url,
blink::mojom::ScreenAvailability screen_availability) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!listeners_.count(url))
return;
listeners_[url]->OnScreenAvailabilityChanged(std::move(screen_availability));
}
void ControllerPresentationServiceDelegateForFuzzing::
HandleSetDefaultPresentationUrls(
const mojolpm::blink::mojom::PresentationConnectionResult&
proto_result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
blink::mojom::PresentationConnectionResultPtr result;
if (!mojolpm::FromProto(proto_result, result))
return;
base::PostTask(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing::
CallSetDefaultPresentationUrls,
GetWeakPtr(), std::move(result)));
}
void ControllerPresentationServiceDelegateForFuzzing::
CallSetDefaultPresentationUrls(
blink::mojom::PresentationConnectionResultPtr result_ptr) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (set_default_presentation_urls_callback_.is_null())
return;
set_default_presentation_urls_callback_.Run(std::move(result_ptr));
}
void ControllerPresentationServiceDelegateForFuzzing::
HandleStartPresentationSuccess(
const mojolpm::blink::mojom::PresentationConnectionResult&
proto_result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
blink::mojom::PresentationConnectionResultPtr result;
if (!mojolpm::FromProto(proto_result, result))
return;
base::PostTask(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing::
CallStartPresentationSuccess,
GetWeakPtr(), std::move(result)));
}
void ControllerPresentationServiceDelegateForFuzzing::
CallStartPresentationSuccess(
blink::mojom::PresentationConnectionResultPtr result_ptr) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (result_ptr->presentation_info->id.length() > kMaxPresentationIdLength)
return;
if (start_presentation_success_cb_.is_null())
return;
std::move(start_presentation_success_cb_).Run(std::move(result_ptr));
}
void ControllerPresentationServiceDelegateForFuzzing::
HandleStartPresentationError(
const mojolpm::blink::mojom::PresentationError& proto_error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
blink::mojom::PresentationErrorPtr error;
if (!mojolpm::FromProto(proto_error, error))
return;
base::PostTask(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing::
CallStartPresentationError,
GetWeakPtr(), std::move(error)));
}
void ControllerPresentationServiceDelegateForFuzzing::
CallStartPresentationError(blink::mojom::PresentationErrorPtr error_ptr) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (start_presentation_error_cb_.is_null())
return;
std::move(start_presentation_error_cb_).Run(*std::move(error_ptr));
}
void ControllerPresentationServiceDelegateForFuzzing::
HandleReconnectPresentationSuccess(
const mojolpm::blink::mojom::PresentationConnectionResult&
proto_result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
blink::mojom::PresentationConnectionResultPtr result;
if (!mojolpm::FromProto(proto_result, result))
return;
base::PostTask(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing::
CallReconnectPresentationSuccess,
GetWeakPtr(), std::move(result)));
}
void ControllerPresentationServiceDelegateForFuzzing::
CallReconnectPresentationSuccess(
blink::mojom::PresentationConnectionResultPtr result_ptr) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (reconnect_presentation_success_cb_.is_null())
return;
std::move(reconnect_presentation_success_cb_).Run(std::move(result_ptr));
}
void ControllerPresentationServiceDelegateForFuzzing::
HandleReconnectPresentationError(
const mojolpm::blink::mojom::PresentationError& proto_error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
blink::mojom::PresentationErrorPtr error;
if (!mojolpm::FromProto(proto_error, error))
return;
base::PostTask(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing::
CallReconnectPresentationError,
GetWeakPtr(), std::move(error)));
}
void ControllerPresentationServiceDelegateForFuzzing::
CallReconnectPresentationError(
blink::mojom::PresentationErrorPtr error_ptr) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (reconnect_presentation_error_cb_.is_null())
return;
std::move(reconnect_presentation_error_cb_).Run(*std::move(error_ptr));
}
void ControllerPresentationServiceDelegateForFuzzing::
HandleListenForConnectionStateChangeStateChanged(
const mojolpm::blink::mojom::PresentationConnectionState&
proto_connection_state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
blink::mojom::PresentationConnectionState connection_state;
if (!mojolpm::FromProto(proto_connection_state, connection_state))
return;
base::PostTask(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing::
CallListenForConnectionStateChangeStateChanged,
GetWeakPtr(), std::move(connection_state)));
}
void ControllerPresentationServiceDelegateForFuzzing::
CallListenForConnectionStateChangeStateChanged(
blink::mojom::PresentationConnectionState connection_state) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (listen_for_connection_state_change_state_changed_cb_.is_null())
return;
content::PresentationConnectionStateChangeInfo state_change_info{
connection_state};
std::move(listen_for_connection_state_change_state_changed_cb_)
.Run(state_change_info);
}
void ControllerPresentationServiceDelegateForFuzzing::NextAction(
const Action& action) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (action.action_case()) {
case Action::kListenerGetAvailabilityUrl: {
HandleListenersGetAvailabilityUrl(
action.listener_get_availability_url().listener_availability_url());
} break;
case Action::kListenerOnScreenAvailabilityChanged: {
HandleListenersOnScreenAvailabilityChanged(
action.listener_on_screen_availability_changed()
.listener_availability_url(),
action.listener_on_screen_availability_changed()
.screen_availability());
} break;
case Action::kSetDefaultPresentationUrls: {
HandleSetDefaultPresentationUrls(action.set_default_presentation_urls()
.presentation_connection_result());
} break;
case Action::kStartPresentationSuccess: {
HandleStartPresentationSuccess(
action.start_presentation_success().presentation_connection_result());
} break;
case Action::kStartPresentationError: {
HandleStartPresentationError(
action.start_presentation_error().presentation_error());
} break;
case Action::kReconnectPresentationSuccess: {
HandleReconnectPresentationSuccess(action.reconnect_presentation_success()
.presentation_connection_result());
} break;
case Action::kReconnectPresentationError: {
HandleReconnectPresentationError(
action.reconnect_presentation_error().presentation_error());
} break;
case Action::kListenForConnectionStateChangeStateChanged: {
HandleListenForConnectionStateChangeStateChanged(
action.listen_for_connection_state_change_state_changed()
.presentation_connection_state());
} break;
case Action::ACTION_NOT_SET:
break;
}
}
// Copyright 2020 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.
#ifndef CONTENT_TEST_FUZZER_CONTROLLER_PRESENTATION_SERVICE_DELEGATE_FOR_FUZZING_H_
#define CONTENT_TEST_FUZZER_CONTROLLER_PRESENTATION_SERVICE_DELEGATE_FOR_FUZZING_H_
#include <map>
#include <memory>
#include <string>
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/presentation_screen_availability_listener.h"
#include "content/public/browser/presentation_service_delegate.h"
#include "content/test/fuzzer/controller_presentation_service_delegate_for_fuzzing.pb.h"
#include "media/base/flinging_controller.h"
#include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
// Fake for the `ControllerPresentationServiceDelegate` interface
//
// This fake is limited to do callback, observer and listener behaviours,
// according to a set of actions specified by a protobuf.
// So it is not suitable for tests. It is designed for a fuzzer.
//
// Usage: call the component as expected as the delegate interface.
// To run the callables provided, use `NextAction`,
// provide a protobuf of the `Action` message - there lists the possible calls.
// This will instruct the fake to run the callbacks accordingly.
//
// The callables are run on the UI thread, as the delegate interface is expected
// to be called on the UI thread.
// In the case of MojoLPM fuzzers, `NextAction` should be called on the fuzzer
// thread.
class ControllerPresentationServiceDelegateForFuzzing
: public content::ControllerPresentationServiceDelegate {
public:
ControllerPresentationServiceDelegateForFuzzing();
~ControllerPresentationServiceDelegateForFuzzing() override;
// Used to run one callable.
// These are set by using this as implementation of the delegate interface.
// The callable is specified by the `action`, as defined in assocciated proto.
//
// All calls must be run on the same sequence
// (for MojoLPM fuzzers, it should be the fuzzer thread)
void NextAction(
const content::fuzzing::
controller_presentation_service_delegate_for_fuzzing::proto::Action&
action);
// ControllerPresentationServiceDelegate implementation
void AddObserver(int render_process_id,
int render_frame_id,
Observer* observer) override;
void RemoveObserver(int render_process_id, int render_frame_id) override;
void Reset(int render_process_id, int render_frame_id) override;
bool AddScreenAvailabilityListener(
int render_process_id,
int render_frame_id,
content::PresentationScreenAvailabilityListener* listener) override;
void RemoveScreenAvailabilityListener(
int render_process_id,
int render_frame_id,
content::PresentationScreenAvailabilityListener* listener) override;
void SetDefaultPresentationUrls(
const content::PresentationRequest& request,
content::DefaultPresentationConnectionCallback callback) override;
void StartPresentation(
const content::PresentationRequest& request,
content::PresentationConnectionCallback success_cb,
content::PresentationConnectionErrorCallback error_cb) override;
void ReconnectPresentation(
const content::PresentationRequest& request,
const std::string& presentation_id,
content::PresentationConnectionCallback success_cb,
content::PresentationConnectionErrorCallback error_cb) override;
void CloseConnection(int render_process_id,
int render_frame_id,
const std::string& presentation_id) override;
void Terminate(int render_process_id,
int render_frame_id,
const std::string& presentation_id) override;
std::unique_ptr<media::FlingingController> GetFlingingController(
int render_process_id,
int render_frame_id,
const std::string& presentation_id) override;
void ListenForConnectionStateChange(
int render_process_id,
int render_frame_id,
const blink::mojom::PresentationInfo& connection,
const content::PresentationConnectionStateChangedCallback&
state_changed_cb) override;
private:
using Action = content::fuzzing::
controller_presentation_service_delegate_for_fuzzing::proto::Action;
// Use to invoke the callbacks & registered callables
// For each, we have two member functions, where the first calls the second.
// The first is prefixed by `Handle`, which takes the raw protobuf datatype,
// and is run on the same thread as `NextAction` (for MojoLPM: fuzzer thread).
// The second is prefixed by `Call` which invokes the call with the converted
// datatype, and is run on the UI thread.
void HandleListenersGetAvailabilityUrl(
const mojolpm::url::mojom::Url& proto_url);
void CallListenersGetAvailabilityUrl(GURL url);
void HandleListenersOnScreenAvailabilityChanged(
const mojolpm::url::mojom::Url& proto_url,
const mojolpm::blink::mojom::ScreenAvailability&
proto_screen_availability);
void CallListenersOnScreenAvailabilityChanged(
GURL url,
blink::mojom::ScreenAvailability screen_availability);
void HandleSetDefaultPresentationUrls(
const mojolpm::blink::mojom::PresentationConnectionResult& proto_result);
void CallSetDefaultPresentationUrls(
blink::mojom::PresentationConnectionResultPtr result_ptr);
void HandleStartPresentationSuccess(
const mojolpm::blink::mojom::PresentationConnectionResult& proto_result);
void CallStartPresentationSuccess(
blink::mojom::PresentationConnectionResultPtr result_ptr);
void HandleStartPresentationError(
const mojolpm::blink::mojom::PresentationError& proto_error);
void CallStartPresentationError(blink::mojom::PresentationErrorPtr error_ptr);
void HandleReconnectPresentationSuccess(
const mojolpm::blink::mojom::PresentationConnectionResult& proto_result);
void CallReconnectPresentationSuccess(
blink::mojom::PresentationConnectionResultPtr result_ptr);
void HandleReconnectPresentationError(
const mojolpm::blink::mojom::PresentationError& proto_error);
void CallReconnectPresentationError(
blink::mojom::PresentationErrorPtr error_ptr);
void HandleListenForConnectionStateChangeStateChanged(
const mojolpm::blink::mojom::PresentationConnectionState&
proto_connection_state);
void CallListenForConnectionStateChangeStateChanged(
blink::mojom::PresentationConnectionState connection_state);
base::WeakPtr<ControllerPresentationServiceDelegateForFuzzing> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
// We store all callbacks as single callables; not queued nor out of order,
// as this mimics the real delegate as closely as possible
std::map<content::GlobalFrameRoutingId,
content::PresentationServiceDelegate::Observer*>
observers_;
std::map<GURL, content::PresentationScreenAvailabilityListener*> listeners_;
content::DefaultPresentationConnectionCallback
set_default_presentation_urls_callback_;
content::PresentationConnectionCallback start_presentation_success_cb_;
content::PresentationConnectionErrorCallback start_presentation_error_cb_;
content::PresentationConnectionCallback reconnect_presentation_success_cb_;
content::PresentationConnectionErrorCallback reconnect_presentation_error_cb_;
content::PresentationConnectionStateChangedCallback
listen_for_connection_state_change_state_changed_cb_;
SEQUENCE_CHECKER(sequence_checker_);
// `PostTask`ing the calls onto the UI thread is not guaranteed to outlive
// `this`. So a weak pointer is necessary for those calls.
base::WeakPtrFactory<ControllerPresentationServiceDelegateForFuzzing>
weak_factory_{this};
};
#endif // CONTENT_TEST_FUZZER_CONTROLLER_PRESENTATION_SERVICE_DELEGATE_FOR_FUZZING_H_
// Copyright 2020 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.
// Message format for `ControllerPresentationServiceDelegateForFuzzing`
// An `Action` will run a callable
syntax = "proto2";
package content.fuzzing.controller_presentation_service_delegate_for_fuzzing.proto;
import "third_party/blink/public/mojom/presentation/presentation.mojom.mojolpm.proto";
import "url/mojom/url.mojom.mojolpm.proto";
// Used to specify actions, which will run the appropriate call onto the
// specified, by url,`PresentationScreenAvailabilityListener`.
// Having been registered with the fake, through the interface as the delegate.
message GetAvailabilityUrlAction {
required mojolpm.url.mojom.Url listener_availability_url = 1;
}
message OnScreenAvailabilityChangedAction {
required mojolpm.url.mojom.Url listener_availability_url = 1;
required mojolpm.blink.mojom.ScreenAvailability screen_availability = 2;
}
// Used to specify actions, which would run a callback established through the
// interface as the delegate
message PresentationConnectionAction {
required mojolpm.blink.mojom.PresentationConnectionResult
presentation_connection_result = 1;
}
message PresentationConnectionErrorAction {
required mojolpm.blink.mojom.PresentationError presentation_error = 1;
}
message PresentationConnectionStateChangedAction {
required mojolpm.blink.mojom.PresentationConnectionState
presentation_connection_state = 1;
}
// Actions that can be performed by the fake.
message Action {
oneof action {
GetAvailabilityUrlAction listener_get_availability_url = 1;
OnScreenAvailabilityChangedAction listener_on_screen_availability_changed =
2;
PresentationConnectionAction set_default_presentation_urls = 3;
PresentationConnectionAction start_presentation_success = 4;
PresentationConnectionErrorAction start_presentation_error = 5;
PresentationConnectionAction reconnect_presentation_success = 6;
PresentationConnectionErrorAction reconnect_presentation_error = 7;
PresentationConnectionStateChangedAction
listen_for_connection_state_change_state_changed = 8;
}
}
// Copyright 2020 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 <stdint.h>
#include <memory>
#include <string>
#include <utility>
#include "base/at_exit.h"
#include "base/base_switches.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/i18n/icu_util.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/task/post_task.h"
#include "base/test/test_switches.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "content/browser/gpu/gpu_data_manager_impl.h" // nogncheck
#include "content/browser/network_service_instance_impl.h" // nogncheck
#include "content/browser/presentation/presentation_service_impl.h" // nogncheck
#include "content/browser/presentation/presentation_test_utils.h"
#include "content/browser/site_instance_impl.h" // nogncheck
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/presentation_request.h"
#include "content/public/browser/presentation_service_delegate.h"
#include "content/public/browser/site_instance.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/mock_navigation_handle.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_content_client_initializer.h"
#include "content/public/test/test_renderer_host.h"
#include "content/test/fuzzer/controller_presentation_service_delegate_for_fuzzing.h"
#include "content/test/fuzzer/presentation_service_mojolpm_fuzzer.pb.h"
#include "content/test/test_render_frame_host.h"
#include "content/test/test_web_contents.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "third_party/blink/public/mojom/presentation/presentation.mojom-mojolpm.h"
#include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h"
#include "ui/events/devices/device_data_manager.h"
const char* cmdline[] = {"presentation_service_mojolpm_fuzzer", nullptr};
// Global environment needed to run the interface being tested.
//
// This will be created once, before fuzzing starts, and will be shared between
// all testcases. It is created on the main thread.
//
// At a minimum, we should always be able to set up the command line, i18n and
// mojo, and create the thread on which the fuzzer will be run. We want to avoid
// (as much as is reasonable) any state being preserved between testcases.
//
// We try to create an environment that matches the real browser process as
// much as possible, so we use real platform threads in the task environment.
class ContentFuzzerEnvironment {
public:
ContentFuzzerEnvironment()
: fuzzer_thread_((base::CommandLine::Init(1, cmdline), "fuzzer_thread")) {
TestTimeouts::Initialize();
logging::SetMinLogLevel(logging::LOG_FATAL);
mojo::core::Init();
base::i18n::InitializeICU();
fuzzer_thread_.StartAndWaitForTesting();
content::ForceCreateNetworkServiceDirectlyForTesting();
}
scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() {
return fuzzer_thread_.task_runner();
}
private:
base::AtExitManager at_exit_manager_;
base::Thread fuzzer_thread_;
content::TestContentClientInitializer content_client_initializer_;
};
ContentFuzzerEnvironment& GetEnvironment() {
static base::NoDestructor<ContentFuzzerEnvironment> environment;
return *environment;
}
scoped_refptr<base::SequencedTaskRunner> GetFuzzerTaskRunner() {
return GetEnvironment().fuzzer_task_runner();
}
// Per-testcase state needed to run the interface being tested.
//
// The lifetime of this is scoped to a single testcase, and it is created and
// destroyed from the fuzzer sequence.
//
// This owns the instance of `PresentationServiceImpl` to be fuzzed, and the
// mock delegate instance that will be used by the service instance.
// This class inherits from `RenderViewHostTestHarness` as the service
// instance relies on using a `RenderFrameHost` instance.
//
// TODO(clovispj) investigate performance loss this causes:
// The test harness has the drawback it owns a `BrowserTaskEnvironment`, so it
// becomes scoped per testcase - it would be preferred to be global to all.
//
// We use a single `PresentationServiceImpl` which can be bound to multiple
// remotes, to imitate true behaviour as much as possbile.
class PresentationServiceTestcase : public content::RenderViewHostTestHarness {
public:
explicit PresentationServiceTestcase(
const content::fuzzing::presentation_service::proto::Testcase& testcase);
~PresentationServiceTestcase() override;
// Returns true once either all of the actions in the testcase have been
// performed, or the per-testcase action limit has been exceeded.
//
// This should only be called from the fuzzer sequence.
bool IsFinished();
// If there are still actions remaining in the testcase, this will perform the
// next sequence of actions before returning.
//
// If IsFinished() would return true, then calling this function is a no-op.
//
// This should only be called from the fuzzer sequence.
void NextAction();
private:
using Action = content::fuzzing::presentation_service::proto::Action;
void SetUp() override;
void SetUpOnUIThread();
void TearDown() override;
void TearDownOnUIThread();
// Create and bind a new instance for fuzzing. This needs to make sure that
// the new instance has been created and bound on the correct sequence before
// returning.
void AddPresentationService(uint32_t id);
void TestBody() override {}
// The proto message describing the test actions to perform.
const content::fuzzing::presentation_service::proto::Testcase& testcase_;
// Apply a reasonable upper-bound on testcase complexity to avoid timeouts.
const int max_action_count_ = 512;
// Apply a reasonable upper-bound on maximum size of action that we will
// deserialize. (This is deliberately slightly larger than max mojo message
// size)
const size_t max_action_size_ = 300 * 1024 * 1024;
// Count of total actions performed in this testcase.
int action_count_ = 0;
// The index of the next sequence of actions to execute.
int next_sequence_idx_ = 0;
// A fake delegate which we can control with protobuf messages,
// the actions of which are also within our fuzzer's actions.
// Required as `PresentationServiceDelegateImpl` expects UI interaction.
std::unique_ptr<ControllerPresentationServiceDelegateForFuzzing>
controller_delegate_;
// Component which we fuzz
std::unique_ptr<content::PresentationServiceImpl> presentation_service_;
SEQUENCE_CHECKER(sequence_checker_);
};
PresentationServiceTestcase::PresentationServiceTestcase(
const content::fuzzing::presentation_service::proto::Testcase& testcase)
: RenderViewHostTestHarness(
base::test::TaskEnvironment::TimeSource::MOCK_TIME,
base::test::TaskEnvironment::MainThreadType::DEFAULT,
base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC,
base::test::TaskEnvironment::ThreadingMode::MULTIPLE_THREADS,
content::BrowserTaskEnvironment::REAL_IO_THREAD),
testcase_(testcase) {
SetUp();
}
PresentationServiceTestcase::~PresentationServiceTestcase() {
TearDown();
}
bool PresentationServiceTestcase::IsFinished() {
return next_sequence_idx_ >= testcase_.sequence_indexes_size();
}
void PresentationServiceTestcase::NextAction() {
if (next_sequence_idx_ < testcase_.sequence_indexes_size()) {
auto sequence_idx = testcase_.sequence_indexes(next_sequence_idx_++);
const auto& sequence =
testcase_.sequences(sequence_idx % testcase_.sequences_size());
for (auto action_idx : sequence.action_indexes()) {
if (!testcase_.actions_size() || ++action_count_ > max_action_count_) {
return;
}
const auto& action =
testcase_.actions(action_idx % testcase_.actions_size());
if (action.ByteSizeLong() > max_action_size_) {
return;
}
switch (action.action_case()) {
case Action::kRunThread: {
if (action.run_thread().id()) {
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
base::PostTask(FROM_HERE, {content::BrowserThread::UI},
run_loop.QuitClosure());
run_loop.Run();
} else {
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
base::PostTask(FROM_HERE, {content::BrowserThread::IO},
run_loop.QuitClosure());
run_loop.Run();
}
} break;
case Action::kNewPresentationService: {
AddPresentationService(action.new_presentation_service().id());
} break;
case Action::kPresentationServiceRemoteAction: {
mojolpm::HandleRemoteAction(
action.presentation_service_remote_action());
} break;
case Action::kPresentationControllerReceiverAction: {
mojolpm::HandleReceiverAction(
action.presentation_controller_receiver_action());
} break;
case Action::kPresentationReceiverReceiverAction: {
mojolpm::HandleReceiverAction(
action.presentation_receiver_receiver_action());
} break;
case Action::kControllerDelegateAction: {
controller_delegate_->NextAction(action.controller_delegate_action());
} break;
case Action::ACTION_NOT_SET:
break;
}
}
}
}
void PresentationServiceTestcase::SetUp() {
RenderViewHostTestHarness::SetUp();
base::RunLoop run_loop;
base::PostTaskAndReply(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&PresentationServiceTestcase::SetUpOnUIThread,
base::Unretained(this)),
run_loop.QuitClosure());
run_loop.Run();
}
void PresentationServiceTestcase::SetUpOnUIThread() {
content::TestRenderFrameHost* render_frame_host =
static_cast<content::TestWebContents*>(web_contents())->GetMainFrame();
render_frame_host->InitializeRenderFrameIfNeeded();
presentation_service_ =
content::PresentationServiceImpl::Create(render_frame_host);
controller_delegate_ =
std::make_unique<ControllerPresentationServiceDelegateForFuzzing>();
presentation_service_->SetControllerDelegateForTesting(
controller_delegate_.get());
}
void PresentationServiceTestcase::TearDown() {
base::RunLoop run_loop;
base::PostTaskAndReply(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&PresentationServiceTestcase::TearDownOnUIThread,
base::Unretained(this)),
run_loop.QuitClosure());
run_loop.Run();
RenderViewHostTestHarness::TearDown();
}
void PresentationServiceTestcase::TearDownOnUIThread() {
presentation_service_.reset();
controller_delegate_.reset();
}
void PresentationServiceTestcase::AddPresentationService(uint32_t id) {
mojo::Remote<::blink::mojom::PresentationService> remote;
auto receiver = remote.BindNewPipeAndPassReceiver();
// `Unretained` is safe here, as `run_loop.Run()` blocks until
// `PostTaskAndReply` calls the quit closure.
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
base::PostTaskAndReply(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&content::PresentationServiceImpl::Bind,
base::Unretained(presentation_service_.get()),
std::move(receiver)),
run_loop.QuitClosure());
run_loop.Run();
mojolpm::GetContext()->AddInstance(id, std::move(remote));
}
// Helper function to keep scheduling fuzzer actions on the current runloop
// until the testcase has completed, and then quit the runloop.
void NextAction(PresentationServiceTestcase* testcase,
base::RepeatingClosure quit_closure) {
if (!testcase->IsFinished()) {
testcase->NextAction();
GetFuzzerTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(NextAction, base::Unretained(testcase),
std::move(quit_closure)));
} else {
GetFuzzerTaskRunner()->PostTask(FROM_HERE, std::move(quit_closure));
}
}
// Helper function to setup and run the testcase, since we need to do that from
// the fuzzer sequence rather than the main thread.
void RunTestcase(PresentationServiceTestcase* testcase) {
mojo::Message message;
auto dispatch_context =
std::make_unique<mojo::internal::MessageDispatchContext>(&message);
mojolpm::GetContext()->StartTestcase();
base::RunLoop fuzzer_run_loop(base::RunLoop::Type::kNestableTasksAllowed);
GetFuzzerTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(NextAction, base::Unretained(testcase),
fuzzer_run_loop.QuitClosure()));
fuzzer_run_loop.Run();
mojolpm::GetContext()->EndTestcase();
}
DEFINE_BINARY_PROTO_FUZZER(
const content::fuzzing::presentation_service::proto::Testcase&
proto_testcase) {
if (!proto_testcase.actions_size() || !proto_testcase.sequences_size() ||
!proto_testcase.sequence_indexes_size()) {
return;
}
// Make sure that the environment is initialized before we do anything else.
GetEnvironment();
PresentationServiceTestcase testcase(proto_testcase);
base::RunLoop ui_run_loop(base::RunLoop::Type::kNestableTasksAllowed);
// Unretained is safe here, because ui_run_loop has to finish before testcase
// goes out of scope.
GetFuzzerTaskRunner()->PostTaskAndReply(
FROM_HERE, base::BindOnce(RunTestcase, base::Unretained(&testcase)),
ui_run_loop.QuitClosure());
ui_run_loop.Run();
}
// Copyright 2020 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.
// Message format for the MojoLPM fuzzer for the PresentationService interface.
syntax = "proto2";
package content.fuzzing.presentation_service.proto;
import "content/test/fuzzer/controller_presentation_service_delegate_for_fuzzing.proto";
import "third_party/blink/public/mojom/presentation/presentation.mojom.mojolpm.proto";
// Bind a new PresentationService remote
message NewPresentationServiceAction {
required uint32 id = 1;
}
// Run the specific sequence for (an indeterminate) period. This is not
// intended to create a specific ordering, but to allow the fuzzer to delay a
// later task until previous tasks have completed.
message RunThreadAction {
enum ThreadId {
IO = 0;
UI = 1;
}
required ThreadId id = 1;
}
// Actions that can be performed by the fuzzer.
message Action {
oneof action {
RunThreadAction run_thread = 1;
NewPresentationServiceAction new_presentation_service = 2;
mojolpm.blink.mojom.PresentationService.RemoteAction
presentation_service_remote_action = 3;
mojolpm.blink.mojom.PresentationController.ReceiverAction
presentation_controller_receiver_action = 4;
mojolpm.blink.mojom.PresentationReceiver.ReceiverAction
presentation_receiver_receiver_action = 5;
content.fuzzing.controller_presentation_service_delegate_for_fuzzing.proto
.Action controller_delegate_action = 6;
}
}
// Sequence provides a level of indirection which allows Testcase to compactly
// express repeated sequences of actions.
message Sequence {
repeated uint32 action_indexes = 1 [packed = true];
}
// Testcase is the top-level message type interpreted by the fuzzer.
message Testcase {
repeated Action actions = 1;
repeated Sequence sequences = 2;
repeated uint32 sequence_indexes = 3 [packed = true];
}
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