#include <stdint.h>
#include <memory>
#include <string>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ref.h"
#include "base/no_destructor.h"
#include "base/run_loop.h"
#include "base/task/sequenced_task_runner.h"
#include "base/threading/thread.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/presentation/presentation_service_impl.h"
#include "content/browser/presentation/presentation_test_utils.h"
#include "content/browser/site_instance_impl.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_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_renderer_host.h"
#include "content/test/fuzzer/controller_presentation_service_delegate_for_fuzzing.h"
#include "content/test/fuzzer/mojolpm_fuzzer_support.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/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"
constexpr const char* kCmdline[] = {"presentation_service_mojolpm_fuzzer",
nullptr};
content::mojolpm::FuzzerEnvironment& GetEnvironment() {
static base::NoDestructor<content::mojolpm::FuzzerEnvironment> environment(
1, kCmdline);
return *environment;
}
scoped_refptr<base::SequencedTaskRunner> GetFuzzerTaskRunner() {
return GetEnvironment().fuzzer_task_runner();
}
class PresentationServiceTestcase : public content::RenderViewHostTestHarness {
public:
explicit PresentationServiceTestcase(
const content::fuzzing::presentation_service::proto::Testcase& testcase);
~PresentationServiceTestcase() override;
bool IsFinished();
void NextAction();
private:
using Action = content::fuzzing::presentation_service::proto::Action;
void SetUp() override;
void SetUpOnUIThread();
void TearDown() override;
void TearDownOnUIThread();
void AddPresentationService(uint32_t id);
void TestBody() override {}
const raw_ref<const content::fuzzing::presentation_service::proto::Testcase>
testcase_;
const int max_action_count_ = 512;
const size_t max_action_size_ = 300 * 1024 * 1024;
int action_count_ = 0;
int next_sequence_idx_ = 0;
std::unique_ptr<ControllerPresentationServiceDelegateForFuzzing>
controller_delegate_;
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);
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
} else {
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE, 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;
content::GetUIThreadTaskRunner({})->PostTaskAndReply(
FROM_HERE,
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())
->GetPrimaryMainFrame();
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;
content::GetUIThreadTaskRunner({})->PostTaskAndReply(
FROM_HERE,
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();
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
content::GetUIThreadTaskRunner({})->PostTaskAndReply(
FROM_HERE,
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));
}
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));
}
}
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;
}
GetEnvironment();
PresentationServiceTestcase testcase(proto_testcase);
base::RunLoop ui_run_loop(base::RunLoop::Type::kNestableTasksAllowed);
GetFuzzerTaskRunner()->PostTaskAndReply(
FROM_HERE, base::BindOnce(RunTestcase, base::Unretained(&testcase)),
ui_run_loop.QuitClosure());
ui_run_loop.Run();
}