rrhce
rrhce

Reputation: 1

V8 crashes when iterating

I've been trying to embed V8 javascript engine to my project. For the most part this has been successful, but there is one problem that doesn't seem to go away and I don't know what causes it.

When I write even a simple for loop to iterate array of few thousand entries, V8 segfaults. Valgrind says this:

==38120== Invalid read of size 8
==38120==    at 0x525F654: v8::internal::Isolate::main_thread_local_heap() (in /home/ktp/oma/lib/liboma_js.so)
==38120==    by 0x53A532A: v8::internal::interpreter::BytecodeArrayIterator::BytecodeArrayIterator(v8::internal::Handle<v8::internal::BytecodeArray>, int) (in /home/ktp/oma/lib/liboma_js.so)
==38120==    by 0x5C01534: v8::internal::baseline::BaselineCompiler::BaselineCompiler(v8::internal::LocalIsolate*, v8::internal::Handle<v8::internal::SharedFunctionInfo>, v8::internal::Handle<v8::internal::BytecodeArray>) (in /home/ktp/oma/lib/liboma_js.so)
==38120==    by 0x5C143E9: v8::internal::GenerateBaselineCode(v8::internal::Isolate*, v8::internal::Handle<v8::internal::SharedFunctionInfo>) (in /home/ktp/oma/lib/liboma_js.so)
==38120==    by 0x51D796E: v8::internal::Compiler::CompileSharedWithBaseline(v8::internal::Isolate*, v8::internal::Handle<v8::internal::SharedFunctionInfo>, v8::internal::Compiler::ClearExceptionFlag, v8::internal::IsCompiledScope*) (in /home/ktp/oma/lib/liboma_js.so)
==38120==    by 0x51D7CF5: v8::internal::Compiler::CompileBaseline(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Compiler::ClearExceptionFlag, v8::internal::IsCompiledScope*) (in /home/ktp/oma/lib/liboma_js.so)
==38120==    by 0x5BFF355: v8::internal::baseline::BaselineBatchCompiler::CompileBatch(v8::internal::Handle<v8::internal::JSFunction>) (in /home/ktp/oma/lib/liboma_js.so)
==38120==    by 0x5BFEF8E: v8::internal::baseline::BaselineBatchCompiler::EnqueueFunction(v8::internal::Handle<v8::internal::JSFunction>) (in /home/ktp/oma/lib/liboma_js.so)
==38120==    by 0x5276501: v8::internal::TieringManager::OnInterruptTick(v8::internal::Handle<v8::internal::JSFunction>) (in /home/ktp/oma/lib/liboma_js.so)
==38120==    by 0x5D8BA69: v8::internal::Runtime_BytecodeBudgetInterruptWithStackCheck(int, unsigned long*, v8::internal::Isolate*) (in /home/ktp/oma/lib/liboma_js.so)
==38120==    by 0x5AEA9F7: Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit (in /home/ktp/oma/lib/liboma_js.so)
==38120==    by 0x5B98E62: Builtins_JumpLoopHandler (in /home/ktp/oma/lib/liboma_js.so)
==38120==  Address 0xf100 is not stack'd, malloc'd or (recently) free'd

My bindings and setup is pretty much based on the examples from v8.dev's guide to embedding, although wrapped in a class and with v8::platform::PumpMessageLoop() called with timerfd in epoll loop which runs in same thread as V8.

The JS code that caused valgrind message was this:

var g = new Array(1024*16);

g.fill(0);

for(var i in g)
{
    i+i;
}

The JS code works when array's size is at max 1024*7 and when I printed the iteration count, it crashed at around 7500.

Edit: this C++ code gives the same error and is pretty much shortened version of the code I am using:

#include <v8.h>
#include <v8-context.h>
#include <v8-isolate.h>
#include <v8-local-handle.h>
#include <v8-primitive.h>
#include <v8-script.h>
#include <v8-json.h>
#include <libplatform/libplatform.h>

int main(int argc, char *argv[])
{    
    // Initialize V8.
    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();
    // 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);
    std::string src = R"(
    for(var i=0;i<1024*8;i++)
    {
    }
    )";

{
    v8::HandleScope handle_scope(isolate); 
    v8::Local<v8::Context> ctx = v8::Context::New(isolate); 
    v8::Context::Scope context_scope(ctx);

    v8::MaybeLocal<v8::String> vsrc = v8::String::NewFromUtf8(isolate, src.c_str(), v8::NewStringType::kNormal, static_cast<int>(src.size()));

    v8::MaybeLocal<v8::Script> script = v8::Script::Compile(ctx, vsrc.ToLocalChecked()).ToLocalChecked();
    if(script.IsEmpty())
    {
        printf("error0\n");
    }
    
    v8::MaybeLocal<v8::Value> result = script.ToLocalChecked()->Run(ctx);
    
    if(result.IsEmpty())
    {
        printf("error1\n");  
    }
}

isolate->Dispose();
v8::V8::Dispose();
v8::V8::DisposePlatform();
delete create_params.array_buffer_allocator;
return 0;
}

Example was compiled with these commands:

g++ -c /home/ktp/oma/src/jx.cpp -DV8_COMPRESS_POINTERS -DV8_ENABLE_SANDBOX -I/home/ktp/v8/v8/include/ -fPIC -Wall -g -std=c++17
g++ -o /home/ktp/oma/bin/jx jx.o -DV8_COMPRESS_POINTERS -DV8_ENABLE_SANDBOX -lv8_monolith -ldl -lpthread -L/home/ktp/v8/v8/out.gn/x64.release.sample/obj/

I am not entirely sure about V8 version, but I built it on 30.8. and d8 console says V8 version 10.7.0.

V8 was built according to the instructions at v8.dev and https://v8.dev/docs/source-code#using-git :

tools/dev/v8gen.py x64.release.sample
ninja -C out.gn/x64.release.sample v8_monolith

gn args says this:

dcheck_always_on = false
is_component_build = false
is_debug = false
target_cpu = "x64"
use_custom_libcxx = false
v8_enable_sandbox = true
v8_monolithic = true
v8_use_external_startup_data = false

Upvotes: 0

Views: 399

Answers (1)

jmrk
jmrk

Reputation: 40561

You need to enter the Isolate. You can either use isolate->Enter() and isolate->Exit(), or a v8::Isolate::Scope isolate_scope(isolate) (which is a convenience wrapper around those two steps), somewhere between creating the Isolate and creating the HandleScope.

To help you help yourself:

  1. Change your GN args (using gn args out/x64.release.sample) to say is_debug = true to get a Debug build, and recompile.
  2. Run the Debug binary in a debugger, e.g. gdb -args /home/ktp/oma/bin/jx (or whichever debugger you prefer to use).
  3. In this case, you don't even really need fancy debugging features, as the Debug build contains a failing check: Debug check failed: (isolate) != nullptr. If you request a backtrace, you'll see that this happens somewhere deep in V8's guts; luckily the specifics don't matter in this case.
  4. Armed with this knowledge, you can compare your code to some working example, e.g. V8's shell sample, and see if it does anything with Isolates that your code isn't doing. The Isolate::Scope stands out as a likely suspect.

Upvotes: 1

Related Questions