Commit a5949822 authored by Clovis PJ's avatar Clovis PJ Committed by Commit Bot

Adding Video Capture Host MojoLPM fuzzer.

Bug: 1113715
Change-Id: Ia927f27bba18eedf56447fa05afd3bddde679331
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2273128
Commit-Queue: Clovis PJ <clovispj@google.com>
Reviewed-by: default avatarGuido Urdaneta <guidou@chromium.org>
Reviewed-by: default avatarMax Moroz <mmoroz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806672}
parent d3c810db
......@@ -254,3 +254,30 @@ fuzzer_test("indexed_db_leveldb_coding_decodeidbkeypath_fuzzer") {
seed_corpus =
"../../browser/indexed_db/fuzzer_corpus/encoded_indexed_db_key_path"
}
# Fuzzer only supports linux. As we use a `V4L2CaptureDevice`, which is only
# usable on linux through `VideoCaptureDeviceFactoryLinux`.
if (is_linux) {
mojolpm_fuzzer_test("video_capture_host_mojolpm_fuzzer") {
sources = [ "video_capture_host_mojolpm_fuzzer.cc" ]
proto_source = "video_capture_host_mojolpm_fuzzer.proto"
deps = [
"//base/test:test_support",
"//content/browser:for_content_tests",
"//content/public/browser:browser",
"//content/test:content_unittests",
"//content/test:test_support",
"//media:test_support",
"//media/capture:capture_lib",
"//media/capture:test_support",
"//services/network:test_support",
"//storage/browser:test_support",
"//third_party/blink/public/common:common",
"//third_party/icu:icudata",
]
proto_deps = [ "//media/capture/mojom:video_capture_mojolpm" ]
}
}
// 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/bind.h"
#include "base/command_line.h"
#include "base/i18n/icu_util.h"
#include "base/no_destructor.h"
#include "base/run_loop.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread.h"
#include "content/browser/renderer_host/media/fake_video_capture_provider.h"
#include "content/browser/renderer_host/media/in_process_video_capture_provider.h" // nogncheck
#include "content/browser/renderer_host/media/media_stream_manager.h" // nogncheck
#include "content/browser/renderer_host/media/media_stream_ui_proxy.h" // nogncheck
#include "content/browser/renderer_host/media/video_capture_host.h" // nogncheck
#include "content/browser/renderer_host/media/video_capture_manager.h" // nogncheck
#include "content/public/browser/browser_task_traits.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_content_client_initializer.h"
#include "content/test/fuzzer/video_capture_host_mojolpm_fuzzer.pb.h"
#include "content/test/test_content_browser_client.h"
#include "media/audio/audio_system_impl.h"
#include "media/audio/mock_audio_manager.h"
#include "media/audio/test_audio_thread.h"
#include "media/capture/mojom/video_capture.mojom-mojolpm.h"
#include "media/capture/video/create_video_capture_device_factory.h"
#include "media/capture/video/linux/fake_device_provider.h"
#include "media/capture/video/linux/fake_v4l2_impl.h"
#include "media/capture/video/linux/video_capture_device_factory_linux.h"
#include "media/capture/video/video_capture_system_impl.h"
#include "media/capture/video_capture_types.h"
#include "mojo/core/embedder/embedder.h"
#include "third_party/blink/public/common/mediastream/media_devices.h"
#include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h"
const char* cmdline[] = {"video_capture_host_mojolpm_fuzzer", nullptr};
// Describe all the devices (as descriptors).
const uint32_t kNumDeviceDescriptors = 4;
const media::VideoCaptureDeviceDescriptors kDeviceDescriptors{
{"dev_name_1", "dev_id_1"},
{"dev_name_2", "dev_id_2"},
{"dev_name_3", "dev_id_3"},
{"dev_name_4", "dev_id_4"}};
// Specifies number of render process ids (counted from 0).
// All devices are opened for each id.
const uint32_t kNumRenderProcessIds = 2;
// 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();
}
scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() {
return fuzzer_thread_.task_runner();
}
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.
//
// The component under fuzz `VideoCaptureHost` is created on call
// `AddVideoCaptureHost` (singly bound using a self owned receiver).
// This directly relies on `MediaStreamManager`, which is owned here (as well as
// indirectly reliant on other components owned here).
//
// `MediaStreamManager` is a `CurrentThread::DestructionObserver`, so it must
// outlive the `BrowserTaskEnvironment`. So the task environment is scoped per
// testcase (undesirable for performance).
class VideoCaptureHostTestcase {
public:
VideoCaptureHostTestcase(
const content::fuzzing::video_capture_host::proto::Testcase& testcase);
~VideoCaptureHostTestcase();
// 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::video_capture_host::proto::Action;
void SetUp();
void SetUpOnIOThreadFirst();
void SetUpOnUIThread();
void SetUpOnIOThreadSecond();
// We want to open video sessions, to enable behaviour for the fuzzer.
// To do this, we enumerate with the `MediaDeviceManager` (on UI thread),
// then `OpenDevice` with the `MediaStreamManager` (on IO thread).
// `OpenSession` is repeated for each `render_process_id`.
// So the same devices are opened for multiple ids.
void OpenSession(int render_process_id,
int render_frame_id,
int requester_id,
int page_request_id);
void OpenSessionOnUIThread(
int render_process_id,
int render_frame_id,
content::MediaDeviceSaltAndOrigin* salt_and_origin);
void OpenSessionOnIOThread(
int render_process_id,
int render_frame_id,
int requester_id,
int page_request_id,
const content::MediaDeviceSaltAndOrigin& salt_and_origin,
base::OnceClosure quit_closure);
void TearDown();
void TearDownOnIOThread();
void TearDownOnUIThread();
std::unique_ptr<content::FakeMediaStreamUIProxy> CreateFakeUI();
// A callback to receive the enumerated devices in the
// `WebMediaDeviceInfoArray`.
void VideoInputDevicesEnumerated(
base::OnceClosure quit_closure,
const std::string& salt,
const url::Origin& security_origin,
blink::WebMediaDeviceInfoArray* out,
const content::MediaDeviceEnumeration& enumeration);
// A callback which confirms opening device success. This provides the
// session ids for the devices, stored in `opened_session_ids_`.
void OnDeviceOpened(base::OnceClosure quit_closure,
int render_process_id,
uint32_t device_index,
bool success,
const std::string& label,
const blink::MediaStreamDevice& opened_device);
// 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 AddVideoCaptureHost(uint32_t id, uint32_t render_process_id);
// This wraps `HandleRemoteAction`, making the call for the correct device.
// As it requires specifying the `render_process_id` and `device_index`.
// to find the correct session id for the device.
void HandleDeviceRemoteAction(
const content::fuzzing::video_capture_host::proto::
VideoCaptureHostDeviceRemoteAction& device_remote_action);
// Used to directly inject the session id into the `RemoteAction`,
// overwriting the protobuf field.
using RemoteAction = mojolpm::media::mojom::VideoCaptureHost_RemoteAction;
const RemoteAction& RemoteActionInjectSessionId(
const RemoteAction& remote_method_action,
const ::base::UnguessableToken& input);
// We register, and therefore use, the devices per `render_process_id`.
// So this gets the appropriate session id.
const base::UnguessableToken& OpenedSessionId(int render_process_id,
uint32_t device_index);
// The proto message describing the test actions to perform.
const content::fuzzing::video_capture_host::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;
// Prerequisite components for the `VideoCaptureHost`.
std::unique_ptr<content::MediaStreamManager> media_stream_manager_;
std::unique_ptr<media::AudioManager> audio_manager_;
std::unique_ptr<media::AudioSystem> audio_system_;
// Indexed by `render_process_id` then `kDeviceDescriptors` index
// See `OpenedSessionId` getter.
std::array<std::array<base::UnguessableToken, kNumDeviceDescriptors>,
kNumRenderProcessIds>
opened_session_ids_;
// Prerequisite state.
content::BrowserTaskEnvironment task_environment_;
content::TestBrowserContext browser_context_;
content::TestContentBrowserClient browser_client_;
SEQUENCE_CHECKER(sequence_checker_);
};
VideoCaptureHostTestcase::VideoCaptureHostTestcase(
const content::fuzzing::video_capture_host::proto::Testcase& testcase)
: testcase_(testcase),
task_environment_(
base::test::TaskEnvironment::MainThreadType::DEFAULT,
base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC,
base::test::TaskEnvironment::ThreadingMode::MULTIPLE_THREADS,
content::BrowserTaskEnvironment::REAL_IO_THREAD),
browser_context_(),
browser_client_() {
// VideoCaptureHostTestcase is created on the main thread, but the actions
// that we want to validate the sequencing of take place on the fuzzer
// sequence.
DETACH_FROM_SEQUENCE(sequence_checker_);
SetUp();
for (uint32_t render_process_id = 0; render_process_id < kNumRenderProcessIds;
render_process_id++)
OpenSession(render_process_id,
/*render_frame_id=*/1,
/*requester_id=*/1,
/*page_request_id=*/1);
}
VideoCaptureHostTestcase::~VideoCaptureHostTestcase() {
TearDown();
}
bool VideoCaptureHostTestcase::IsFinished() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return next_sequence_idx_ >= testcase_.sequence_indexes_size();
}
void VideoCaptureHostTestcase::NextAction() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
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::kNewVideoCaptureHost: {
AddVideoCaptureHost(
action.new_video_capture_host().id(),
action.new_video_capture_host().render_process_id());
} break;
case Action::kVideoCaptureHostDeviceRemoteAction: {
HandleDeviceRemoteAction(
action.video_capture_host_device_remote_action());
} break;
case Action::kVideoCaptureObserverReceiverAction: {
mojolpm::HandleReceiverAction(
action.video_capture_observer_receiver_action());
} break;
case Action::ACTION_NOT_SET:
break;
}
}
}
}
void VideoCaptureHostTestcase::SetUp() {
{
base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed};
base::PostTaskAndReply(
FROM_HERE, {content::BrowserThread::IO},
base::BindOnce(&VideoCaptureHostTestcase::SetUpOnIOThreadFirst,
base::Unretained(this)),
run_loop.QuitClosure());
run_loop.Run();
}
{
base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed};
base::PostTaskAndReply(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&VideoCaptureHostTestcase::SetUpOnUIThread,
base::Unretained(this)),
run_loop.QuitClosure());
run_loop.Run();
}
{
base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed};
base::PostTaskAndReply(
FROM_HERE, {content::BrowserThread::IO},
base::BindOnce(&VideoCaptureHostTestcase::SetUpOnIOThreadSecond,
base::Unretained(this)),
run_loop.QuitClosure());
run_loop.Run();
}
}
void VideoCaptureHostTestcase::SetUpOnIOThreadFirst() {
audio_manager_ = std::make_unique<media::MockAudioManager>(
std::make_unique<media::TestAudioThread>());
audio_system_ =
std::make_unique<media::AudioSystemImpl>(audio_manager_.get());
}
void VideoCaptureHostTestcase::SetUpOnUIThread() {
// Here we specify the devices described by `kDeviceDescriptors`.
// Which tells the `VideoCaptureDeviceFactoryLinux` what devices we have.
// This factory is then used to setup the `MediaStreamManager`.
std::unique_ptr<media::FakeDeviceProvider> fake_device_provider =
std::make_unique<media::FakeDeviceProvider>();
scoped_refptr<media::FakeV4L2Impl> fake_v4l2_impl =
base::MakeRefCounted<media::FakeV4L2Impl>();
for (const auto& descriptor : kDeviceDescriptors) {
// Note, despite the param name, `device_name` should match `device_id`
fake_v4l2_impl->AddDevice(/*device_name=*/descriptor.device_id,
media::FakeV4L2DeviceConfig(descriptor));
fake_device_provider->AddDevice(descriptor);
}
std::unique_ptr<media::VideoCaptureDeviceFactoryLinux>
video_capture_device_factory =
std::make_unique<media::VideoCaptureDeviceFactoryLinux>(
task_environment_.GetMainThreadTaskRunner());
video_capture_device_factory->SetV4L2EnvironmentForTesting(
std::move(fake_v4l2_impl), std::move(fake_device_provider));
media_stream_manager_ = std::make_unique<content::MediaStreamManager>(
audio_system_.get(), audio_manager_->GetTaskRunner(),
content::InProcessVideoCaptureProvider::CreateInstance(
std::make_unique<media::VideoCaptureSystemImpl>(
std::move(video_capture_device_factory)),
audio_manager_->GetTaskRunner(), base::DoNothing()));
}
void VideoCaptureHostTestcase::SetUpOnIOThreadSecond() {
media_stream_manager_->UseFakeUIFactoryForTests(base::BindRepeating(
&VideoCaptureHostTestcase::CreateFakeUI, base::Unretained(this)));
}
void VideoCaptureHostTestcase::OpenSession(int render_process_id,
int render_frame_id,
int requester_id,
int page_request_id) {
// We get `salt_and_origin` on the UI Thread, and use it on the IO thread.
content::MediaDeviceSaltAndOrigin salt_and_origin;
{
base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed};
base::PostTaskAndReply(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&VideoCaptureHostTestcase::OpenSessionOnUIThread,
base::Unretained(this), render_process_id,
render_frame_id, base::Unretained(&salt_and_origin)),
run_loop.QuitClosure());
run_loop.Run();
}
{
base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed};
base::PostTask(
FROM_HERE, {content::BrowserThread::IO},
base::BindOnce(&VideoCaptureHostTestcase::OpenSessionOnIOThread,
base::Unretained(this), render_process_id,
render_frame_id, requester_id, page_request_id,
salt_and_origin, run_loop.QuitClosure()));
run_loop.Run();
}
}
void VideoCaptureHostTestcase::OpenSessionOnUIThread(
int render_process_id,
int render_frame_id,
content::MediaDeviceSaltAndOrigin* out_salt_and_origin) {
*out_salt_and_origin =
content::GetMediaDeviceSaltAndOrigin(render_process_id, render_frame_id);
}
void VideoCaptureHostTestcase::OpenSessionOnIOThread(
int render_process_id,
int render_frame_id,
int requester_id,
int page_request_id,
const content::MediaDeviceSaltAndOrigin& salt_and_origin,
base::OnceClosure quit_closure) {
// Enumerate video devices.
blink::WebMediaDeviceInfoArray video_devices;
{
base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed};
content::MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
media_stream_manager_->media_devices_manager()->EnumerateDevices(
devices_to_enumerate,
base::BindOnce(&VideoCaptureHostTestcase::VideoInputDevicesEnumerated,
base::Unretained(this), run_loop.QuitClosure(),
salt_and_origin.device_id_salt, salt_and_origin.origin,
&video_devices));
run_loop.Run();
}
// Open video devices.
for (uint32_t device_index = 0; device_index < video_devices.size();
device_index++) {
base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed};
media_stream_manager_->OpenDevice(
render_process_id, render_frame_id, requester_id, page_request_id,
video_devices[device_index].device_id,
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, salt_and_origin,
base::BindOnce(&VideoCaptureHostTestcase::OnDeviceOpened,
base::Unretained(this), run_loop.QuitClosure(),
render_process_id, device_index),
content::MediaStreamManager::DeviceStoppedCallback());
run_loop.Run();
}
std::move(quit_closure).Run();
}
void VideoCaptureHostTestcase::TearDown() {
{
base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed};
base::PostTaskAndReply(
FROM_HERE, {content::BrowserThread::IO},
base::BindOnce(&VideoCaptureHostTestcase::TearDownOnIOThread,
base::Unretained(this)),
run_loop.QuitClosure());
run_loop.Run();
}
{
base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed};
base::PostTaskAndReply(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&VideoCaptureHostTestcase::TearDownOnUIThread,
base::Unretained(this)),
run_loop.QuitClosure());
run_loop.Run();
}
}
void VideoCaptureHostTestcase::TearDownOnIOThread() {
audio_manager_->Shutdown();
audio_manager_.reset();
}
void VideoCaptureHostTestcase::TearDownOnUIThread() {
audio_system_.reset();
}
std::unique_ptr<content::FakeMediaStreamUIProxy>
VideoCaptureHostTestcase::CreateFakeUI() {
return std::make_unique<content::FakeMediaStreamUIProxy>(
/*tests_use_fake_render_frame_hosts=*/true);
}
void VideoCaptureHostTestcase::VideoInputDevicesEnumerated(
base::OnceClosure quit_closure,
const std::string& salt,
const url::Origin& security_origin,
blink::WebMediaDeviceInfoArray* out,
const content::MediaDeviceEnumeration& enumeration) {
for (const auto& info : enumeration[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT]) {
std::string device_id =
content::MediaStreamManager::GetHMACForMediaDeviceID(
salt, security_origin, info.device_id);
out->push_back(
blink::WebMediaDeviceInfo(device_id, info.label, std::string()));
}
std::move(quit_closure).Run();
}
void VideoCaptureHostTestcase::OnDeviceOpened(
base::OnceClosure quit_closure,
int render_process_id,
uint32_t device_index,
bool success,
const std::string& label,
const blink::MediaStreamDevice& opened_device) {
if (success) {
opened_session_ids_[render_process_id][device_index] =
opened_device.session_id();
}
std::move(quit_closure).Run();
}
void VideoCaptureHostTestcase::AddVideoCaptureHost(uint32_t id,
uint32_t render_process_id) {
mojo::Remote<::media::mojom::VideoCaptureHost> remote;
auto receiver = remote.BindNewPipeAndPassReceiver();
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
base::PostTaskAndReply(
FROM_HERE, {content::BrowserThread::IO},
base::BindOnce(&content::VideoCaptureHost::Create, render_process_id,
media_stream_manager_.get(), std::move(receiver)),
run_loop.QuitClosure());
run_loop.Run();
mojolpm::GetContext()->AddInstance(id, std::move(remote));
}
/* Matches signature of a mojolpm::ToProto implementation, which has not been
* implemented yet, to be replaced once that is done.
* see "mojo/public/mojom/base/unguessable_token.mojom-mojolpm.h"
*/
bool ToProto(const ::base::UnguessableToken& input,
mojolpm::mojo_base::mojom::UnguessableToken& output) {
if (output.has_new_()) {
output.mutable_new_()->set_id(1UL);
output.mutable_new_()->set_m_low(input.GetLowForSerialization());
output.mutable_new_()->set_m_high(input.GetHighForSerialization());
return true;
}
auto allocated_new = std::make_unique<
mojolpm::mojo_base::mojom::UnguessableToken_ProtoStruct>();
allocated_new->set_id(1UL);
allocated_new->set_m_low(input.GetLowForSerialization());
allocated_new->set_m_high(input.GetHighForSerialization());
// passes ownership of `allocated_new`
output.set_allocated_new_(allocated_new.release());
return true;
}
void VideoCaptureHostTestcase::HandleDeviceRemoteAction(
const content::fuzzing::video_capture_host::proto::
VideoCaptureHostDeviceRemoteAction& device_remote_action) {
const base::UnguessableToken& token =
OpenedSessionId(device_remote_action.render_process_id(),
device_remote_action.device_index());
mojolpm::HandleRemoteAction(
RemoteActionInjectSessionId(device_remote_action.remote_action(), token));
}
const VideoCaptureHostTestcase::RemoteAction&
VideoCaptureHostTestcase::RemoteActionInjectSessionId(
const RemoteAction& remote_method_action,
const ::base::UnguessableToken& token) {
// `const_cast` used for performance (could also copy)
RemoteAction& remote_method_action_mutable =
const_cast<RemoteAction&>(remote_method_action);
mojolpm::mojo_base::mojom::UnguessableToken* m_session_id;
switch (remote_method_action_mutable.method_case()) {
case RemoteAction::kMStart:
m_session_id = remote_method_action_mutable.mutable_m_start()
->mutable_m_session_id();
break;
case RemoteAction::kMResume:
m_session_id = remote_method_action_mutable.mutable_m_resume()
->mutable_m_session_id();
break;
case RemoteAction::kMGetDeviceSupportedFormats:
m_session_id =
remote_method_action_mutable.mutable_m_get_device_supported_formats()
->mutable_m_session_id();
break;
case RemoteAction::kMGetDeviceFormatsInUse:
m_session_id =
remote_method_action_mutable.mutable_m_get_device_formats_in_use()
->mutable_m_session_id();
break;
default:
return remote_method_action;
}
if (!ToProto(token, *m_session_id))
return remote_method_action;
return remote_method_action_mutable;
}
const base::UnguessableToken& VideoCaptureHostTestcase::OpenedSessionId(
int render_process_id,
uint32_t device_index) {
return opened_session_ids_[render_process_id % kNumRenderProcessIds]
[device_index % kNumDeviceDescriptors];
}
// Helper function to keep scheduling fuzzer actions on the current runloop
// until the testcase has completed, and then quit the runloop.
void NextAction(VideoCaptureHostTestcase* 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(VideoCaptureHostTestcase* 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::video_capture_host::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();
VideoCaptureHostTestcase 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();
}
syntax = "proto2";
package content.fuzzing.video_capture_host.proto;
import "media/capture/mojom/video_capture.mojom.mojolpm.proto";
// Bind a new VideoCaptureHost remote
message NewVideoCaptureHostAction {
required uint32 id = 1;
required uint32 render_process_id = 2;
}
// A wrapper message for all remote actions. It requires further specifying
// the `render_process_id` and `device_index` to identify which session id to
// inject into the call. So the call correctly acts for a specific device.
message VideoCaptureHostDeviceRemoteAction {
required uint32 render_process_id = 1;
required uint32 device_index = 2;
required mojolpm.media.mojom.VideoCaptureHost.RemoteAction remote_action = 3;
}
// 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;
NewVideoCaptureHostAction new_video_capture_host = 2;
VideoCaptureHostDeviceRemoteAction video_capture_host_device_remote_action =
3;
mojolpm.media.mojom.VideoCaptureObserver.ReceiverAction
video_capture_observer_receiver_action = 4;
}
}
// 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