Commit 756873c6 authored by Chris Cunningham's avatar Chris Cunningham Committed by Commit Bot

WebCodecs: Fuzzing VideoEncoder

Bug: 1120745
Change-Id: I88e11996a22f1aef2bf53d967e9cf728b517dc24
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2370182
Auto-Submit: Chrome Cunningham <chcunningham@chromium.org>
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Reviewed-by: default avatarJeremy Roman <jbroman@chromium.org>
Reviewed-by: default avatarThomas Guilbert <tguilbert@chromium.org>
Cr-Commit-Position: refs/heads/master@{#801115}
parent 1565dbb1
......@@ -590,4 +590,22 @@ if (use_libfuzzer) {
"//third_party/protobuf:protobuf_lite",
]
}
fuzzer_test("webcodecs_video_encoder_fuzzer") {
sources = [
"webcodecs/fuzzer_utils.cc",
"webcodecs/fuzzer_utils.h",
"webcodecs/video_encoder_fuzzer.cc",
]
seed_corpus = "webcodecs/fuzzer_seed_corpus/video_encoder"
deps = [
":modules",
"//third_party/blink/renderer/modules/webcodecs:fuzzer_protos",
"//third_party/blink/renderer/platform:blink_fuzzer_test_support",
"//third_party/libprotobuf-mutator",
"//third_party/protobuf:protobuf_lite",
]
}
}
......@@ -27,15 +27,16 @@ specific_include_rules = {
"+testing/libfuzzer/proto/lpm_interface.h",
"+third_party/protobuf/src/google/protobuf/repeated_field.h",
],
"audio_decoder_fuzzer.cc": [
".*_decoder_fuzzer.cc": [
"+base/run_loop.h",
"+testing/libfuzzer/proto/lpm_interface.h",
],
"video_decoder_fuzzer.cc": [
"video_encoder_fuzzer.cc": [
"+base/at_exit.h",
"+base/run_loop.h",
"+testing/libfuzzer/proto/lpm_interface.h",
],
"canvas_fuzzer.cc": [
"+base/test/bind_test_util.h",
]
}
\ No newline at end of file
}
......@@ -13,6 +13,26 @@ message ConfigureVideoDecoder {
optional bytes description = 2;
}
message ConfigureVideoEncoder {
// String describing codec (e.g. "vp09.00.10.08")
optional string codec = 1;
enum EncoderAccelerationPreference {
ALLOW = 0;
DENY = 1;
REQUIRE = 2;
}
optional EncoderAccelerationPreference acceleration = 2;
optional uint64 bitrate = 3;
optional double framerate = 4;
optional uint32 width = 5;
optional uint32 height = 6;
}
message ConfigureAudioDecoder {
// String describing codec (e.g. "opus")
optional string codec = 1;
......@@ -47,6 +67,25 @@ message EncodedAudioChunk {
optional bytes data = 4;
}
// Mix of args to VideoFrame constructor.
// TODO(sandersd): Define a new Init type when PlanesInit constructor lands.
message VideoFrameBitmapInit {
optional uint64 timestamp = 1;
optional uint64 duration = 2;
optional uint32 bitmap_width = 3;
optional bytes rgb_bitmap = 4;
}
message EncodeVideo {
optional VideoFrameBitmapInit frame = 1;
message EncodeOptions { optional bool key_frame = 1; }
optional EncodeOptions options = 2;
}
message DecodeVideo {
optional EncodedVideoChunk chunk = 1;
}
......@@ -90,3 +129,17 @@ message AudioDecoderApiInvocation {
message AudioDecoderApiInvocationSequence {
repeated AudioDecoderApiInvocation invocations = 1;
}
message VideoEncoderApiInvocation {
oneof Api {
ConfigureVideoEncoder configure = 1;
EncodeVideo encode = 2;
Flush flush = 3;
Reset reset = 4;
Close close = 5;
}
}
message VideoEncoderApiInvocationSequence {
repeated VideoEncoderApiInvocation invocations = 1;
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -6,11 +6,20 @@
#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_decoder_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_frame_init.h"
#include "third_party/blink/renderer/core/html/canvas/image_data.h"
#include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/modules/webcodecs/fuzzer_inputs.pb.h"
#include "third_party/blink/renderer/modules/webcodecs/video_frame.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include <string>
......@@ -47,7 +56,7 @@ EncodedVideoConfig* MakeVideoDecoderConfig(
EncodedAudioConfig* MakeAudioDecoderConfig(
const wc_fuzzer::ConfigureAudioDecoder& proto) {
auto* config = EncodedAudioConfig::Create();
EncodedAudioConfig* config = EncodedAudioConfig::Create();
config->setCodec(proto.codec().c_str());
config->setSampleRate(proto.sample_rate());
config->setNumberOfChannels(proto.number_of_channels());
......@@ -60,6 +69,34 @@ EncodedAudioConfig* MakeAudioDecoderConfig(
return config;
}
VideoEncoderConfig* MakeEncoderConfig(
const wc_fuzzer::ConfigureVideoEncoder& proto) {
VideoEncoderConfig* config = VideoEncoderConfig::Create();
config->setCodec(proto.codec().c_str());
config->setAcceleration(ToAccelerationType(proto.acceleration()));
config->setFramerate(proto.framerate());
config->setWidth(proto.width());
config->setHeight(proto.height());
// Bitrate is truly optional, so don't just take the proto default value.
if (proto.has_bitrate())
config->setBitrate(proto.bitrate());
return config;
}
String ToAccelerationType(
wc_fuzzer::ConfigureVideoEncoder_EncoderAccelerationPreference type) {
switch (type) {
case wc_fuzzer::ConfigureVideoEncoder_EncoderAccelerationPreference_ALLOW:
return "allow";
case wc_fuzzer::ConfigureVideoEncoder_EncoderAccelerationPreference_DENY:
return "deny";
case wc_fuzzer::ConfigureVideoEncoder_EncoderAccelerationPreference_REQUIRE:
return "require";
}
}
String ToChunkType(wc_fuzzer::EncodedChunkType type) {
switch (type) {
case wc_fuzzer::EncodedChunkType::KEY:
......@@ -87,4 +124,37 @@ EncodedAudioChunk* MakeEncodedAudioChunk(
data_copy);
}
VideoEncoderEncodeOptions* MakeEncodeOptions(
const wc_fuzzer::EncodeVideo_EncodeOptions& proto) {
VideoEncoderEncodeOptions* options = VideoEncoderEncodeOptions::Create();
// Truly optional, so don't set it if its just a proto default value.
if (proto.has_key_frame())
options->setKeyFrame(proto.key_frame());
return options;
}
VideoFrame* MakeVideoFrame(const wc_fuzzer::VideoFrameBitmapInit& proto) {
NotShared<DOMUint8ClampedArray> data_u8(DOMUint8ClampedArray::Create(
reinterpret_cast<const unsigned char*>(proto.rgb_bitmap().data()),
proto.rgb_bitmap().size()));
ImageData* image_data = ImageData::Create(data_u8, proto.bitmap_width(),
IGNORE_EXCEPTION_FOR_TESTING);
if (!image_data)
return nullptr;
ImageBitmap* image_bitmap = MakeGarbageCollected<ImageBitmap>(
image_data, base::nullopt, ImageBitmapOptions::Create());
VideoFrameInit* video_frame_init = VideoFrameInit::Create();
video_frame_init->setTimestamp(proto.timestamp());
video_frame_init->setDuration(proto.duration());
return VideoFrame::Create(image_bitmap, video_frame_init,
IGNORE_EXCEPTION_FOR_TESTING);
}
} // namespace blink
......@@ -10,10 +10,13 @@
#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_decoder_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_encode_options.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.h"
#include "third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.h"
#include "third_party/blink/renderer/modules/webcodecs/fuzzer_inputs.pb.h"
#include "third_party/blink/renderer/modules/webcodecs/video_frame.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
......@@ -40,12 +43,23 @@ EncodedVideoConfig* MakeVideoDecoderConfig(
EncodedAudioConfig* MakeAudioDecoderConfig(
const wc_fuzzer::ConfigureAudioDecoder& proto);
VideoEncoderConfig* MakeEncoderConfig(
const wc_fuzzer::ConfigureVideoEncoder& proto);
EncodedVideoChunk* MakeEncodedVideoChunk(
const wc_fuzzer::EncodedVideoChunk& proto);
EncodedAudioChunk* MakeEncodedAudioChunk(
const wc_fuzzer::EncodedAudioChunk& proto);
VideoFrame* MakeVideoFrame(const wc_fuzzer::VideoFrameBitmapInit& proto);
VideoEncoderEncodeOptions* MakeEncodeOptions(
const wc_fuzzer::EncodeVideo_EncodeOptions& proto);
String ToAccelerationType(
wc_fuzzer::ConfigureVideoEncoder_EncoderAccelerationPreference type);
String ToChunkType(wc_fuzzer::EncodedChunkType type);
} // namespace blink
......
// 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 "base/at_exit.h"
#include "base/run_loop.h"
#include "testing/libfuzzer/proto/lpm_interface.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_output_callback.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_web_codecs_error_callback.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.h"
#include "third_party/blink/renderer/modules/webcodecs/fuzzer_inputs.pb.h"
#include "third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h"
#include "third_party/blink/renderer/modules/webcodecs/video_encoder.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#include <string>
#include "third_party/protobuf/src/google/protobuf/text_format.h"
namespace blink {
DEFINE_TEXT_PROTO_FUZZER(
const wc_fuzzer::VideoEncoderApiInvocationSequence& proto) {
static BlinkFuzzerTestSupport test_support = BlinkFuzzerTestSupport();
static DummyPageHolder* page_holder = []() {
auto page_holder = std::make_unique<DummyPageHolder>();
page_holder->GetFrame().GetSettings()->SetScriptEnabled(true);
return page_holder.release();
}();
// Some Image related classes that use base::Singleton will expect this to
// exist for registering exit callbacks (e.g. DarkModeImageClassifier).
base::AtExitManager exit_manager;
//
// NOTE: GC objects that need to survive iterations of the loop below
// must be Persistent<>!
//
// GC may be triggered by the RunLoop().RunUntilIdle() below, which will GC
// raw pointers on the stack. This is not required in production code because
// GC typically runs at the top of the stack, or is conservative enough to
// keep stack pointers alive.
//
// Scoping Persistent<> refs so GC can collect these at the end.
{
Persistent<ScriptState> script_state =
ToScriptStateForMainWorld(&page_holder->GetFrame());
ScriptState::Scope scope(script_state);
Persistent<FakeFunction> error_function =
FakeFunction::Create(script_state, "error");
Persistent<V8WebCodecsErrorCallback> error_callback =
V8WebCodecsErrorCallback::Create(error_function->Bind());
Persistent<FakeFunction> output_function =
FakeFunction::Create(script_state, "output");
Persistent<V8VideoEncoderOutputCallback> output_callback =
V8VideoEncoderOutputCallback::Create(output_function->Bind());
Persistent<VideoEncoderInit> video_encoder_init =
MakeGarbageCollected<VideoEncoderInit>();
video_encoder_init->setError(error_callback);
video_encoder_init->setOutput(output_callback);
Persistent<VideoEncoder> video_encoder = VideoEncoder::Create(
script_state, video_encoder_init, IGNORE_EXCEPTION_FOR_TESTING);
for (auto& invocation : proto.invocations()) {
switch (invocation.Api_case()) {
case wc_fuzzer::VideoEncoderApiInvocation::kConfigure:
video_encoder->configure(MakeEncoderConfig(invocation.configure()),
IGNORE_EXCEPTION_FOR_TESTING);
break;
case wc_fuzzer::VideoEncoderApiInvocation::kEncode: {
VideoFrame* frame = MakeVideoFrame(invocation.encode().frame());
// Often the fuzzer input will be too crazy to produce a valid frame
// (e.g. bitmap width > bitmap length). In these cases, return early
// to discourage this sort of fuzzer input. WebIDL doesn't allow
// callers to pass null, so this is not a real concern.
if (!frame)
return;
video_encoder->encode(
frame, MakeEncodeOptions(invocation.encode().options()),
IGNORE_EXCEPTION_FOR_TESTING);
break;
}
case wc_fuzzer::VideoEncoderApiInvocation::kFlush: {
// TODO(https://crbug.com/1119253): Fuzz whether to await resolution
// of the flush promise.
video_encoder->flush(IGNORE_EXCEPTION_FOR_TESTING);
break;
}
case wc_fuzzer::VideoEncoderApiInvocation::kReset:
video_encoder->reset(IGNORE_EXCEPTION_FOR_TESTING);
break;
case wc_fuzzer::VideoEncoderApiInvocation::kClose:
video_encoder->close(IGNORE_EXCEPTION_FOR_TESTING);
break;
case wc_fuzzer::VideoEncoderApiInvocation::API_NOT_SET:
break;
}
// Give other tasks a chance to run (e.g. calling our output callback).
base::RunLoop().RunUntilIdle();
}
}
// Request a V8 GC. Oilpan will be invoked by the GC epilogue.
//
// Multiple GCs may be required to ensure everything is collected (due to
// a chain of persistent handles), so some objects may not be collected until
// a subsequent iteration. This is slow enough as is, so we compromise on one
// major GC, as opposed to the 5 used in V8GCController for unit tests.
V8PerIsolateData::MainThreadIsolate()->RequestGarbageCollectionForTesting(
v8::Isolate::kFullGarbageCollection);
}
} // namespace blink
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