libv8:在 SIGINT 处理程序中运行 JS 代码时,C++ 程序有时会出现段错误(在 Ctrl+C 之后)

问题描述 投票:0回答:1

我有一个在终端运行并使用 libv8 的 C++ 程序。我想抓住 Ctrl+C 并在退出之前进行一些检查和清理。为了捕获 Ctrl+C,我使用 signal(SIGINT, intHandler),它可以工作,但偶尔会出现段错误。这是最小的演示(只需按住 Ctrl+C,大约 100 次后捕获它的段错误,更复杂的程序更频繁地出现段错误,这是简单的演示,因此它崩溃的频率较低,但仍然会崩溃)。

#include <stdio.h>
#include <signal.h>
#include <node/v8.h>
#include <v8/libplatform/libplatform.h>

const char* ToCString(const v8::String::Utf8Value& value) {//, char * place
    return *value ? *value : "<string conversion failed>";
}

void echo(const v8::FunctionCallbackInfo < v8::Value > &args) {
    // Print first argument
    v8::HandleScope handle_scope(args.GetIsolate());
    v8::String::Utf8Value str(args.GetIsolate(), args[0]);
    const char *cstr = ToCString(str);
    printf("%s\n", cstr);
    fflush(stdout);
};

v8::Isolate* globalIsolate;
v8::Local<v8::Context> globalContext;

void intHandler(int dummy) {
    // compile script
    v8::TryCatch try_catch(globalIsolate);
    v8::Local<v8::String> source = v8::String::NewFromUtf8(globalIsolate, "echo('ctrl+c was pressed')");
    v8::Local<v8::Script> script;
    if (!v8::Script::Compile(globalContext, source).ToLocal(&script)) {
        fprintf(stderr, "error 1: compiled failed!\n");
    }
    // Run the script to get the result.
    v8::Local<v8::Value> result;
    if (!script->Run(globalContext).ToLocal(&result)) {
        fprintf(stderr, "error 2: run failed!\n");
    }
}

int main(int argc, char* argv[]) {
    signal(SIGINT, intHandler);
    // 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();
    v8::Isolate::CreateParams create_params;
    create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
    v8::Isolate* isolate = v8::Isolate::New(create_params);
    v8::Isolate::Scope isolate_scope(isolate);
    globalIsolate = isolate;
    v8::HandleScope handle_scope(isolate);
    v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
    global->Set(isolate, "echo", v8::FunctionTemplate::New(isolate, echo));
    v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
    v8::Context::Scope context_scope(context);
    globalContext = context;
    // compile script
    v8::TryCatch try_catch(isolate);
    v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate, "var x = 1; while (true) { x++; if (x % 10000000 === 0) echo(x); };");
    v8::Local<v8::Script> script;
    if (!v8::Script::Compile(context, source).ToLocal(&script)) {
        fprintf(stderr, "error 3: compiled failed!\n");
        return 1;
    }
    // Run the script to get the result.
    v8::Local<v8::Value> result;
    if (!script->Run(context).ToLocal(&result)) {
        fprintf(stderr, "error 4: run failed!\n");
        return 2;
    }
    // Dispose the isolate and tear down V8.
    //isolate->Dispose();
    //v8::V8::Dispose();
    //v8::V8::DisposePlatform();
    //delete create_params.array_buffer_allocator;
    printf("Program finished\n");
    return 0;
}

我编译:

g++ -o demo demo.cpp -lv8 -lpthread -lssl -lcrypto -Wall -ggdb -I/usr/include/v8

运行程序并按住ctrl+c,直到出现段错误。

我认为当我在 sigint 处理程序中时,我需要以不同的方式运行 JS 代码,但我不知道如何操作。谢谢。

备注:

  • 我使用的是 Ubuntu 22.04.4

  • libv8/libnode 7.8.279.23-node.56

  • g++ 11.4.0

  • 我不知道如何进行 gdb 堆栈跟踪,因为如果我使用 gdb,它会干扰 ctrl+c

  • 我使用了 -fsanitize=address,undefined 并获得了指向 v8 深处某处的堆栈跟踪:

    =================================================================
    ==403170==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000027 (pc 0x73d794d71461 bp 0x7ffee62e53f0 sp 0x7ffee62e5200 T0)
    ==403170==The signal is caused by a READ memory access.
    ==403170==Hint: address points to the zero page.
    #0 0x73d794d71461 in     v8::internal::Compiler::GetSharedFunctionInfoForScript(v8::internal::Isolate*, v8::internal::Handle<v8::internal::String>, v8::internal::Compiler::ScriptDetails const&, v8::ScriptOriginOptions, v8::Extension*, v8::internal::ScriptData*, v8::ScriptCompiler::CompileOptions, v8::ScriptCompiler::NoCacheReason, v8::internal::NativesFlag) (/lib/x86_64-linux-gnu/libnode.so.72+0xd71461)
    #1 0x73d794cc8ccf in v8::ScriptCompiler::CompileUnboundInternal(v8::Isolate*, v8::ScriptCompiler::Source*, v8::ScriptCompiler::CompileOptions, v8::ScriptCompiler::NoCacheReason) (/lib/x86_64-linux-gnu/libnode.so.72+0xcc8ccf)
    #2 0x73d794cc91ad in v8::ScriptCompiler::Compile(v8::Local<v8::Context>, v8::ScriptCompiler::Source*, v8::ScriptCompiler::CompileOptions, v8::ScriptCompiler::NoCacheReason) (/lib/x86_64-linux-gnu/libnode.so.72+0xcc91ad)
    #3 0x73d794cc9260 in v8::Script::Compile(v8::Local<v8::Context>, v8::Local<v8::String>, v8::ScriptOrigin*) (/lib/x86_64-linux-gnu/libnode.so.72+0xcc9260)
    #4 0x5eaac0640466 in intHandler(int) /home/me/demo.cpp:27
    #5 0x73d792a4251f  (/lib/x86_64-linux-gnu/libc.so.6+0x4251f)
    #6 0x17913f4c31c0  (<unknown module>)
    

however demo.cpp:27 is this line inside the intHandler:

    if (!v8::Script::Compile(globalContext, source).ToLocal(&script)) {

c++ v8 libv8
1个回答
0
投票

(这里是 V8 开发者。)

坦率地说,我很惊讶这竟然有效,甚至有时也是如此。这绝对不是受支持的用例:V8 不是为此设计的,也没有对此进行测试,并且没有安全或正确的方法来执行此操作。信号可能到达任意时间点,从而在任意状态下中断执行,并且 V8 的任何部分(特别是:不是堆栈)都没有构建支持这一点。

简而言之:不要在信号处理程序中运行 JS 代码。甚至不要打电话

v8::Script::Compile
。不要执行任何在托管堆上分配的操作,例如
v8::String::NewFromUtf8

(或者这样做,并享受撞车的乐趣——正如模因所说,我只是一个推荐,而不是警察。)

您可以使用

Ctrl+C
功能,尤其是处理
v8::Isolate::TerminateExecution()
时。在内部,它会抛出一种特殊的异常,将 JS 执行展开到代码中最近的
script->Run(...)
,即您应该到达
fprintf(stderr, "error 4: run failed!\n");
行。然后,您可以运行任何您想要运行的检查并正常终止该进程。

© www.soinside.com 2019 - 2024. All rights reserved.