xref: /aosp_15_r20/external/cronet/testing/libfuzzer/fuzzers/v8_fuzzer.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2017 The Chromium Authors
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 <chrono>
6 #include <functional>
7 #include <iostream>
8 #include <mutex>
9 #include <thread>
10 #include <tuple>
11 
12 #include "base/compiler_specific.h"
13 #include "base/memory/raw_ptr.h"
14 #include "v8/include/libplatform/libplatform.h"
15 #include "v8/include/v8.h"
16 
17 using v8::MaybeLocal;
18 using std::ref;
19 using std::lock_guard;
20 using std::mutex;
21 using std::chrono::time_point;
22 using std::chrono::steady_clock;
23 using std::chrono::seconds;
24 using std::chrono::duration_cast;
25 
26 static const seconds kSleepSeconds(1);
27 
28 // Because of the sleep we do, the actual max will be:
29 // kSleepSeconds + kMaxExecutionSeconds.
30 // TODO(metzman): Determine if having such a short timeout causes too much
31 // indeterminism.
32 static const seconds kMaxExecutionSeconds(7);
33 
34 // Inspired by/copied from d8 code, this allocator will return nullptr when
35 // an allocation request is made that puts currently_allocated_ over
36 // kAllocationLimit (1 GB). Should handle the current allocations done by V8.
37 class MockArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
38   std::unique_ptr<Allocator> allocator_ =
39       std::unique_ptr<Allocator>(NewDefaultAllocator());
40 
41   const size_t kAllocationLimit = 1000 * 1024 * 1024;
42   // TODO(metzman): Determine if this approach where we keep track of state
43   // between runs is a good idea. Maybe we should simply prevent allocations
44   // over a certain size regardless of previous allocations.
45   size_t currently_allocated_;
46   mutex mtx_;
47 
48  public:
MockArrayBufferAllocator()49   MockArrayBufferAllocator()
50       : v8::ArrayBuffer::Allocator(), currently_allocated_(0) {}
51 
Allocate(size_t length)52   void* Allocate(size_t length) override {
53     lock_guard<mutex> mtx_locker(mtx_);
54     if (length + currently_allocated_ > kAllocationLimit) {
55       return nullptr;
56     }
57     currently_allocated_ += length;
58     return allocator_->Allocate(length);
59   }
60 
AllocateUninitialized(size_t length)61   void* AllocateUninitialized(size_t length) override {
62     lock_guard<mutex> mtx_locker(mtx_);
63     if (length + currently_allocated_ > kAllocationLimit) {
64       return nullptr;
65     }
66     currently_allocated_ += length;
67     return allocator_->AllocateUninitialized(length);
68   }
69 
Free(void * ptr,size_t length)70   void Free(void* ptr, size_t length) override {
71     lock_guard<mutex> mtx_locker(mtx_);
72     currently_allocated_ -= length;
73     return allocator_->Free(ptr, length);
74   }
75 };
76 
terminate_execution(v8::Isolate * isolate,mutex & mtx,bool & is_running,time_point<steady_clock> & start_time)77 void terminate_execution(v8::Isolate* isolate,
78                          mutex& mtx,
79                          bool& is_running,
80                          time_point<steady_clock>& start_time) {
81   while (true) {
82     std::this_thread::sleep_for(kSleepSeconds);
83     lock_guard<mutex> mtx_locker(mtx);
84     if (is_running) {
85       if (duration_cast<seconds>(steady_clock::now() - start_time) >
86           kMaxExecutionSeconds) {
87         isolate->TerminateExecution();
88         is_running = false;
89         std::cout << "Terminated" << std::endl;
90         fflush(0);
91       }
92     }
93   }
94 }
95 
96 struct Environment {
EnvironmentEnvironment97   Environment() {
98     platform_ = v8::platform::NewDefaultPlatform(
99         0, v8::platform::IdleTaskSupport::kDisabled,
100         v8::platform::InProcessStackDumping::kDisabled, nullptr);
101 
102     v8::V8::InitializePlatform(platform_.get());
103     v8::V8::Initialize();
104     v8::Isolate::CreateParams create_params;
105 
106     mock_arraybuffer_allocator = std::make_unique<MockArrayBufferAllocator>();
107 
108     create_params.array_buffer_allocator = mock_arraybuffer_allocator.get();
109     isolate = v8::Isolate::New(create_params);
110     terminator_thread = std::thread(terminate_execution, isolate, ref(mtx),
111                                     ref(is_running), ref(start_time));
112   }
113   std::unique_ptr<MockArrayBufferAllocator> mock_arraybuffer_allocator;
114   mutex mtx;
115   std::thread terminator_thread;
116   raw_ptr<v8::Isolate> isolate;
117   std::unique_ptr<v8::Platform> platform_;
118   time_point<steady_clock> start_time;
119   bool is_running = true;
120 };
121 
122 // Explicitly specify some attributes to avoid issues with the linker dead-
123 // stripping the following function on macOS, as it is not called directly
124 // by fuzz target. LibFuzzer runtime uses dlsym() to resolve that function.
125 extern "C" __attribute__((used)) __attribute__((visibility("default"))) int
LLVMFuzzerInitialize(int * argc,char *** argv)126 LLVMFuzzerInitialize(int* argc, char*** argv) {
127   v8::V8::InitializeICUDefaultLocation((*argv)[0]);
128   v8::V8::InitializeExternalStartupData((*argv)[0]);
129   v8::V8::SetFlagsFromCommandLine(argc, *argv, true);
130   return 0;
131 }
132 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)133 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
134   static Environment* env = new Environment();
135 
136   if (size < 1)
137     return 0;
138 
139   v8::Isolate::Scope isolate_scope(env->isolate);
140   v8::HandleScope handle_scope(env->isolate);
141   v8::Local<v8::Context> context = v8::Context::New(env->isolate);
142   v8::Context::Scope context_scope(context);
143 
144   std::string source_string =
145       std::string(reinterpret_cast<const char*>(data), size);
146 
147   MaybeLocal<v8::String> source_v8_string = v8::String::NewFromUtf8(
148       env->isolate, source_string.c_str(), v8::NewStringType::kNormal);
149 
150   if (source_v8_string.IsEmpty())
151     return 0;
152 
153   v8::TryCatch try_catch(env->isolate);
154   MaybeLocal<v8::Script> script =
155       v8::Script::Compile(context, source_v8_string.ToLocalChecked());
156 
157   if (script.IsEmpty())
158     return 0;
159 
160   auto local_script = script.ToLocalChecked();
161   env->mtx.lock();
162   env->start_time = steady_clock::now();
163   env->mtx.unlock();
164 
165   std::ignore = local_script->Run(context);
166 
167   lock_guard<mutex> mtx_locker(env->mtx);
168   env->is_running = false;
169   return 0;
170 }
171