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