File: video_encoder_fuzzer.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (147 lines) | stat: -rw-r--r-- 6,418 bytes parent folder | download | duplicates (6)
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