1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/at_exit.h"
6 #include "base/run_loop.h"
7 #include "testing/libfuzzer/proto/lpm_interface.h"
8 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
9 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_decoder_config.h"
10 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_init.h"
11 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_output_callback.h"
12 #include "third_party/blink/renderer/bindings/modules/v8/v8_web_codecs_error_callback.h"
13 #include "third_party/blink/renderer/core/frame/local_frame.h"
14 #include "third_party/blink/renderer/core/frame/settings.h"
15 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
16 #include "third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.h"
17 #include "third_party/blink/renderer/modules/webcodecs/fuzzer_inputs.pb.h"
18 #include "third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h"
19 #include "third_party/blink/renderer/modules/webcodecs/video_encoder.h"
20 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
21 #include "third_party/blink/renderer/platform/bindings/script_state.h"
22 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
23 #include "third_party/blink/renderer/platform/heap/persistent.h"
24 #include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
25 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
26 #include "third_party/blink/renderer/platform/wtf/vector.h"
27
28 #include <string>
29
30 #include "third_party/protobuf/src/google/protobuf/text_format.h"
31
32 namespace blink {
33
DEFINE_TEXT_PROTO_FUZZER(const wc_fuzzer::VideoEncoderApiInvocationSequence & proto)34 DEFINE_TEXT_PROTO_FUZZER(
35 const wc_fuzzer::VideoEncoderApiInvocationSequence& proto) {
36 static BlinkFuzzerTestSupport test_support = BlinkFuzzerTestSupport();
37 static DummyPageHolder* page_holder = []() {
38 auto page_holder = std::make_unique<DummyPageHolder>();
39 page_holder->GetFrame().GetSettings()->SetScriptEnabled(true);
40 return page_holder.release();
41 }();
42
43 // Some Image related classes that use base::Singleton will expect this to
44 // exist for registering exit callbacks (e.g. DarkModeImageClassifier).
45 base::AtExitManager exit_manager;
46
47 //
48 // NOTE: GC objects that need to survive iterations of the loop below
49 // must be Persistent<>!
50 //
51 // GC may be triggered by the RunLoop().RunUntilIdle() below, which will GC
52 // raw pointers on the stack. This is not required in production code because
53 // GC typically runs at the top of the stack, or is conservative enough to
54 // keep stack pointers alive.
55 //
56
57 // Scoping Persistent<> refs so GC can collect these at the end.
58 {
59 Persistent<ScriptState> script_state =
60 ToScriptStateForMainWorld(&page_holder->GetFrame());
61 ScriptState::Scope scope(script_state);
62
63 Persistent<FakeFunction> error_function =
64 FakeFunction::Create(script_state, "error");
65 Persistent<V8WebCodecsErrorCallback> error_callback =
66 V8WebCodecsErrorCallback::Create(error_function->Bind());
67 Persistent<FakeFunction> output_function =
68 FakeFunction::Create(script_state, "output");
69 Persistent<V8VideoEncoderOutputCallback> output_callback =
70 V8VideoEncoderOutputCallback::Create(output_function->Bind());
71
72 Persistent<VideoEncoderInit> video_encoder_init =
73 MakeGarbageCollected<VideoEncoderInit>();
74 video_encoder_init->setError(error_callback);
75 video_encoder_init->setOutput(output_callback);
76
77 Persistent<VideoEncoder> video_encoder = VideoEncoder::Create(
78 script_state, video_encoder_init, IGNORE_EXCEPTION_FOR_TESTING);
79
80 for (auto& invocation : proto.invocations()) {
81 switch (invocation.Api_case()) {
82 case wc_fuzzer::VideoEncoderApiInvocation::kConfigure:
83 video_encoder->configure(MakeEncoderConfig(invocation.configure()),
84 IGNORE_EXCEPTION_FOR_TESTING);
85 break;
86 case wc_fuzzer::VideoEncoderApiInvocation::kEncode: {
87 VideoFrame* frame = MakeVideoFrame(invocation.encode().frame());
88 // Often the fuzzer input will be too crazy to produce a valid frame
89 // (e.g. bitmap width > bitmap length). In these cases, return early
90 // to discourage this sort of fuzzer input. WebIDL doesn't allow
91 // callers to pass null, so this is not a real concern.
92 if (!frame)
93 return;
94
95 video_encoder->encode(
96 frame, MakeEncodeOptions(invocation.encode().options()),
97 IGNORE_EXCEPTION_FOR_TESTING);
98 break;
99 }
100 case wc_fuzzer::VideoEncoderApiInvocation::kFlush: {
101 // TODO(https://crbug.com/1119253): Fuzz whether to await resolution
102 // of the flush promise.
103 video_encoder->flush(IGNORE_EXCEPTION_FOR_TESTING);
104 break;
105 }
106 case wc_fuzzer::VideoEncoderApiInvocation::kReset:
107 video_encoder->reset(IGNORE_EXCEPTION_FOR_TESTING);
108 break;
109 case wc_fuzzer::VideoEncoderApiInvocation::kClose:
110 video_encoder->close(IGNORE_EXCEPTION_FOR_TESTING);
111 break;
112 case wc_fuzzer::VideoEncoderApiInvocation::API_NOT_SET:
113 break;
114 }
115
116 // Give other tasks a chance to run (e.g. calling our output callback).
117 base::RunLoop().RunUntilIdle();
118 }
119 }
120
121 // Request a V8 GC. Oilpan will be invoked by the GC epilogue.
122 //
123 // Multiple GCs may be required to ensure everything is collected (due to
124 // a chain of persistent handles), so some objects may not be collected until
125 // a subsequent iteration. This is slow enough as is, so we compromise on one
126 // major GC, as opposed to the 5 used in V8GCController for unit tests.
127 V8PerIsolateData::MainThreadIsolate()->RequestGarbageCollectionForTesting(
128 v8::Isolate::kFullGarbageCollection);
129 }
130
131 } // namespace blink
132