1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
// Copyright 2020 The Chromium Authors
// 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/video_encoder.h"
#include <string>
#include "base/run_loop.h"
#include "testing/libfuzzer/proto/lpm_interface.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.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_chunk_output_callback.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_decoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_webcodecs_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/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.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/testing/task_environment.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#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();
test::TaskEnvironment task_environment;
auto page_holder = std::make_unique<DummyPageHolder>();
page_holder->GetFrame().GetSettings()->SetScriptEnabled(true);
//
// 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<V8WebCodecsErrorCallback> error_callback =
V8WebCodecsErrorCallback::Create(
MakeGarbageCollected<FakeFunction>("error")->ToV8Function(
script_state));
Persistent<V8EncodedVideoChunkOutputCallback> output_callback =
V8EncodedVideoChunkOutputCallback::Create(
MakeGarbageCollected<FakeFunction>("output")->ToV8Function(
script_state));
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);
if (video_encoder) {
for (auto& invocation : proto.invocations()) {
switch (invocation.Api_case()) {
case wc_fuzzer::VideoEncoderApiInvocation::kConfigure: {
VideoEncoderConfig* config =
MakeVideoEncoderConfig(invocation.configure());
// Use the same config to fuzz isConfigSupported().
VideoEncoder::isConfigSupported(script_state, config,
IGNORE_EXCEPTION_FOR_TESTING);
video_encoder->configure(config, IGNORE_EXCEPTION_FOR_TESTING);
break;
}
case wc_fuzzer::VideoEncoderApiInvocation::kEncode: {
VideoFrame* frame;
switch (invocation.encode().Frames_case()) {
case wc_fuzzer::EncodeVideo::kFrame:
frame = MakeVideoFrame(script_state, invocation.encode().frame());
break;
case wc_fuzzer::EncodeVideo::kFrameFromBuffer:
frame = MakeVideoFrame(script_state,
invocation.encode().frame_from_buffer());
break;
default:
frame = nullptr;
break;
}
// 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();
}
// Let's wait for VideoEncoder to finish its job and give it a
// opportunity to crash, otherwise we might quit too quickly and miss
// something bad happening in a background thread.
ExceptionState exception_state(nullptr);
auto promise = video_encoder->flush(exception_state);
if (!exception_state.HadException()) {
ScriptPromiseTester tester(script_state, promise);
tester.WaitUntilSettled();
}
}
}
} // namespace blink
|