Reputation: 133
I am getting started with embedding v8 and I am encountering some 'unexpected behavior'. The following code produces a Segmentation fault (core dumped)
when the variable value_
is not Reset
at the end (see comment in code). However, the same does not apply for the context context_
. Why? This answer seems to be related but does not provide an explanation.
My expectation was that isolate->Dispose()
takes care of both.
#include <stdlib.h>
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
int main(int argc, char* argv[]) {
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
{
// Initialize V8.
// Create a new Isolate and make it the current one.
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
v8::Global<v8::Context> context_;
v8::Global<v8::String> value_;
{
// Global Context Setup
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
context_.Reset(isolate, context);
// Global Value Setup
v8::Context::Scope context_scope(context);
v8::Local<v8::String> value = v8::String::NewFromUtf8(isolate, "segfault", v8::NewStringType::kNormal).ToLocalChecked();
value_.Reset(isolate, value);
}
// value_.Reset(); // <- Why is this line needed?
// context_.Reset(); // <- Why is this line NOT needed?
isolate->Dispose();
delete create_params.array_buffer_allocator;
}
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
return 0;
}
Build setup:
Follow the instruction in Run the example from the official Getting started with embedding V8. Save the code to sample/wasm.cc and execute following commands:
$ g++ -I. -O2 -Iinclude samples/segfault.cc -o segfault -lv8_monolith -Lout.gn/x64.release.sample/obj/ -pthread -std=c++17
$ ./segfault
Upvotes: 3
Views: 620
Reputation: 16424
v8::Global
has a destructor that will call Reset()
.
The global handles are held in the Isolate
, after Isolate::Dispose()
, the global handles will be freed.
Thus, if you don't call Global::Reset()
, but Dispose
the Isolate
before the destruction of a Global
, the destructor of Global
will cause a access-after-free, which is a typical Undefined Behavior.
Reset()
will set the internal pointer to nullptr
, and subsequent call will check this fact and doesn't do anything. That's why you can add a Reset()
before Dispose()
to avoid the UB.
That's also true for your Global<Context>
, it doesn't demonstrate itself because access-after-free doesn't always trigger a segfault.
Upvotes: 3