Commit 76b01ae8 authored by Chris Cunningham's avatar Chris Cunningham Committed by Commit Bot

WebCodecs: Fuzzing VideoDecoder

Change-Id: I82a28a93cdec01e587c2f281561af041010b9446
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2364075
Commit-Queue: Chrome Cunningham <chcunningham@chromium.org>
Reviewed-by: default avatarJeremy Roman <jbroman@chromium.org>
Reviewed-by: default avatarThomas Guilbert <tguilbert@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800291}
parent d6bf2029
...@@ -553,4 +553,22 @@ if (use_libfuzzer) { ...@@ -553,4 +553,22 @@ if (use_libfuzzer) {
"//third_party/protobuf:protobuf_lite", "//third_party/protobuf:protobuf_lite",
] ]
} }
fuzzer_test("webcodecs_video_decoder_fuzzer") {
sources = [
"webcodecs/fuzzer_utils.cc",
"webcodecs/fuzzer_utils.h",
"webcodecs/video_decoder_fuzzer.cc",
]
seed_corpus = "webcodecs/fuzzer_seed_corpus/video_decoder"
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,6 +27,10 @@ specific_include_rules = { ...@@ -27,6 +27,10 @@ specific_include_rules = {
"+testing/libfuzzer/proto/lpm_interface.h", "+testing/libfuzzer/proto/lpm_interface.h",
"+third_party/protobuf/src/google/protobuf/repeated_field.h", "+third_party/protobuf/src/google/protobuf/repeated_field.h",
], ],
"video_decoder_fuzzer.cc": [
"+base/run_loop.h",
"+testing/libfuzzer/proto/lpm_interface.h",
],
"canvas_fuzzer.cc": [ "canvas_fuzzer.cc": [
"+base/test/bind_test_util.h", "+base/test/bind_test_util.h",
] ]
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
# found in the LICENSE file. # found in the LICENSE file.
import("//third_party/blink/renderer/modules/modules.gni") import("//third_party/blink/renderer/modules/modules.gni")
import("//third_party/libprotobuf-mutator/fuzzable_proto_library.gni")
import("//third_party/protobuf/proto_library.gni")
blink_modules_sources("webcodecs") { blink_modules_sources("webcodecs") {
sources = [ sources = [
...@@ -84,3 +86,7 @@ source_set("unit_tests") { ...@@ -84,3 +86,7 @@ source_set("unit_tests") {
data = [ "//third_party/blink/web_tests/images/resources/" ] data = [ "//third_party/blink/web_tests/images/resources/" ]
} }
fuzzable_proto_library("fuzzer_protos") {
sources = [ "fuzzer_inputs.proto" ]
}
// 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.
syntax = "proto2";
package wc_fuzzer;
message ConfigureVideoDecoder {
// String describing codec (e.g. "vp09.00.10.08")
optional string codec = 1;
optional bytes description = 2;
}
message EncodedVideoChunk {
enum EncodedVideoChunkType {
KEY = 0;
DELTA = 1;
}
optional EncodedVideoChunkType type = 1;
optional uint64 timestamp = 2;
optional uint64 duration = 3;
optional bytes data = 4;
}
message DecodeVideo {
optional EncodedVideoChunk chunk = 1;
}
message Flush {
optional bool wait_for_promise = 1;
}
message Reset {}
message Close {}
message VideoDecoderApiInvocation {
oneof Api {
ConfigureVideoDecoder configure = 1;
DecodeVideo decode = 2;
Flush flush = 3;
Reset reset = 4;
Close close = 5;
}
}
message VideoDecoderApiInvocationSequence {
repeated VideoDecoderApiInvocation invocations = 1;
}
// 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 "third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h"
#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/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/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/modules/webcodecs/fuzzer_inputs.pb.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include <string>
namespace blink {
// static
FakeFunction* FakeFunction::Create(ScriptState* script_state,
std::string name) {
return MakeGarbageCollected<FakeFunction>(script_state, name);
}
FakeFunction::FakeFunction(ScriptState* script_state, std::string name)
: ScriptFunction(script_state), name_(name) {}
v8::Local<v8::Function> FakeFunction::Bind() {
return BindToV8Function();
}
ScriptValue FakeFunction::Call(ScriptValue) {
return ScriptValue();
}
EncodedVideoConfig* MakeDecoderConfig(
const wc_fuzzer::ConfigureVideoDecoder& proto) {
auto* config = EncodedVideoConfig::Create();
config->setCodec(proto.codec().c_str());
DOMArrayBuffer* data_copy = DOMArrayBuffer::Create(
proto.description().data(), proto.description().size());
config->setDescription(
ArrayBufferOrArrayBufferView::FromArrayBuffer(data_copy));
return config;
}
String ToChunkType(wc_fuzzer::EncodedVideoChunk_EncodedVideoChunkType type) {
switch (type) {
case wc_fuzzer::EncodedVideoChunk_EncodedVideoChunkType_KEY:
return "key";
case wc_fuzzer::EncodedVideoChunk_EncodedVideoChunkType_DELTA:
return "delta";
}
}
EncodedVideoChunk* MakeEncodedVideoChunk(
const wc_fuzzer::EncodedVideoChunk& proto) {
DOMArrayBuffer* data_copy =
DOMArrayBuffer::Create(proto.data().data(), proto.data().size());
return EncodedVideoChunk::Create(ToChunkType(proto.type()), proto.timestamp(),
proto.duration(), data_copy);
}
} // 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_FUZZER_UTILS_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_FUZZER_UTILS_H_
#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/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/core/typed_arrays/dom_array_buffer.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/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include <string>
namespace blink {
class FakeFunction : public ScriptFunction {
public:
static FakeFunction* Create(ScriptState* script_state, std::string name);
explicit FakeFunction(ScriptState* script_state, std::string name);
v8::Local<v8::Function> Bind();
ScriptValue Call(ScriptValue) override;
private:
const std::string name_;
};
EncodedVideoConfig* MakeDecoderConfig(
const wc_fuzzer::ConfigureVideoDecoder& proto);
EncodedVideoChunk* MakeEncodedVideoChunk(
const wc_fuzzer::EncodedVideoChunk& proto);
String ToChunkType(wc_fuzzer::EncodedVideoChunk_EncodedVideoChunkType type);
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_FUZZER_UTILS_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.
#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_decoder_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_frame_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_decoder.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>
namespace blink {
DEFINE_TEXT_PROTO_FUZZER(
const wc_fuzzer::VideoDecoderApiInvocationSequence& 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();
}();
//
// 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<V8VideoFrameOutputCallback> output_callback =
V8VideoFrameOutputCallback::Create(output_function->Bind());
Persistent<VideoDecoderInit> video_decoder_init =
MakeGarbageCollected<VideoDecoderInit>();
video_decoder_init->setError(error_callback);
video_decoder_init->setOutput(output_callback);
Persistent<VideoDecoder> video_decoder = VideoDecoder::Create(
script_state, video_decoder_init, IGNORE_EXCEPTION_FOR_TESTING);
for (auto& invocation : proto.invocations()) {
switch (invocation.Api_case()) {
case wc_fuzzer::VideoDecoderApiInvocation::kConfigure:
video_decoder->configure(MakeDecoderConfig(invocation.configure()),
IGNORE_EXCEPTION_FOR_TESTING);
break;
case wc_fuzzer::VideoDecoderApiInvocation::kDecode:
video_decoder->decode(
MakeEncodedVideoChunk(invocation.decode().chunk()),
IGNORE_EXCEPTION_FOR_TESTING);
break;
case wc_fuzzer::VideoDecoderApiInvocation::kFlush: {
// TODO(https://crbug.com/1119253): Fuzz whether to await resolution
// of the flush promise.
video_decoder->flush(IGNORE_EXCEPTION_FOR_TESTING);
break;
}
case wc_fuzzer::VideoDecoderApiInvocation::kReset:
video_decoder->reset(IGNORE_EXCEPTION_FOR_TESTING);
break;
case wc_fuzzer::VideoDecoderApiInvocation::kClose:
video_decoder->close();
break;
case wc_fuzzer::VideoDecoderApiInvocation::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
...@@ -946,6 +946,16 @@ _CONFIG = [ ...@@ -946,6 +946,16 @@ _CONFIG = [
'libyuv::.+', 'libyuv::.+',
] ]
}, },
{
'paths': [
'third_party/blink/renderer/modules/webcodecs/video_decoder_fuzzer.cc',
'third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc',
'third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h',
],
'allowed': [
'wc_fuzzer::.+',
]
},
{ {
'paths': [ 'paths': [
'third_party/blink/renderer/modules/webgpu/', 'third_party/blink/renderer/modules/webgpu/',
......
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