summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_fuzzer.cc
blob: adf921cd4cdc75d6c1608c63432fc0eb1016ed03 (plain)
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
// Copyright 2016 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/bindings/core/v8/serialization/serialized_script_value.h"

#include <algorithm>
#include <cstddef>
#include <cstdint>

#include "base/numerics/safe_conversions.h"
#include "build/build_config.h"
#include "third_party/blink/public/platform/web_blob_info.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/messaging/message_port.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.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/testing/blink_fuzzer_test_support.h"
#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
#include "v8/include/v8.h"

namespace blink {

namespace {

// Intentionally leaked during fuzzing.
// See testing/libfuzzer/efficient_fuzzer.md.
DummyPageHolder* g_page_holder = nullptr;
WebBlobInfoArray* g_blob_info_array = nullptr;

enum : uint32_t {
  kFuzzMessagePorts = 1 << 0,
  kFuzzBlobInfo = 1 << 1,
};

}  // namespace

int LLVMFuzzerInitialize(int* argc, char*** argv) {
  const char kExposeGC[] = "--expose_gc";
  v8::V8::SetFlagsFromString(kExposeGC, sizeof(kExposeGC));
  static BlinkFuzzerTestSupport fuzzer_support =
      BlinkFuzzerTestSupport(*argc, *argv);
  g_page_holder = DummyPageHolder::Create().release();
  g_page_holder->GetFrame().GetSettings()->SetScriptEnabled(true);
  g_blob_info_array = new WebBlobInfoArray();
  g_blob_info_array->emplace_back(WebBlobInfo::BlobForTesting(
      "d875dfc2-4505-461b-98fe-0cf6cc5eaf44", "text/plain", 12));
  g_blob_info_array->emplace_back(
      WebBlobInfo::FileForTesting("d875dfc2-4505-461b-98fe-0cf6cc5eaf44",
                                  "/native/path", "path", "text/plain"));
  return 0;
}

int LLVMFuzzerTestOneInput(const uint8_t* data, size_t data_size) {
  // Odd sizes are handled in various ways, depending how they arrive.
  // Let's not worry about that case here.
  if (data_size % sizeof(UChar))
    return 0;

  // Truncate the input.
  wtf_size_t size = base::saturated_cast<wtf_size_t>(data_size);

  // Used to control what kind of extra data is provided to the deserializer.
  unsigned hash = StringHasher::HashMemory(data, size);

  SerializedScriptValue::DeserializeOptions options;

  // If message ports are requested, make some.
  if (hash & kFuzzMessagePorts) {
    MessagePortArray* message_ports = MakeGarbageCollected<MessagePortArray>(3);
    std::generate(message_ports->begin(), message_ports->end(), []() {
      MessagePort* port = MessagePort::Create(g_page_holder->GetDocument());
      port->Entangle(mojo::MessagePipe().handle0);
      return port;
    });
    options.message_ports = message_ports;
  }

  // If blobs are requested, supply blob info.
  options.blob_info = (hash & kFuzzBlobInfo) ? g_blob_info_array : nullptr;

  // Set up.
  ScriptState* script_state =
      ToScriptStateForMainWorld(&g_page_holder->GetFrame());
  v8::Isolate* isolate = script_state->GetIsolate();
  ScriptState::Scope scope(script_state);
  v8::TryCatch try_catch(isolate);

  // Deserialize.
  scoped_refptr<SerializedScriptValue> serialized_script_value =
      SerializedScriptValue::Create(reinterpret_cast<const char*>(data), size);
  serialized_script_value->Deserialize(isolate, options);
  CHECK(!try_catch.HasCaught())
      << "deserialize() should return null rather than throwing an exception.";

  // 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);

  return 0;
}

}  // namespace blink

// Explicitly specify some attributes to avoid issues with the linker dead-
// stripping the following function on macOS, as it is not called directly
// by fuzz target. LibFuzzer runtime uses dlsym() to resolve that function.
#if defined(OS_MACOSX)
__attribute__((used)) __attribute__((visibility("default")))
#endif  // defined(OS_MACOSX)
extern "C" int
LLVMFuzzerInitialize(int* argc, char*** argv) {
  return blink::LLVMFuzzerInitialize(argc, argv);
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  return blink::LLVMFuzzerTestOneInput(data, size);
}