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