Skip to content

Issue: SIGSEGV on sequential unit tests #1381

Closed
@jasonterando

Description

Hi, I'm encountering an issue running rusty v8, version 0.82.0, on Linux (x86 64 bit). I'm writing a crate library that integrates v8 to implement a scripting feature. I've created a function to execute V8 scripts. In the function, I'm using sync::Once to ensure the platform is initialized only once. After that, I create an isolate, scope, compile code, etc. and it's fine. I can call the function more than once and it all works perfectly.

However, if I have two unit tests that call that function, I get a SIGSEGV: invalid memory reference. My first thought was there was some sort of threading issue, but running with --test-threads=1 doesn't help. Below I've included a sample file demonstrating the issue.

I don't think I'm doing anything wrong, but I'm a Rust and V8 newbie, so any advice is appreciated. Obviously, writing unit tests to call V8 is a fringe case, but I want to make sure I'm not doing something else that is causing the SIGSEGV's once I release my library.

You can exercise by running the following:

  • cargo test # executes two calls to v8 in the same function
  • cargo test -F individual # executes two unit tests each calling V8, crashes with SIGSEGV
  • cargo test -F individual -- --test-threads=1 # executes two unit tests each calling V8 with one thread, crashes with SIGSEGV

lib.rs

use std::sync::Once;

use uuid::Uuid;

static V8_INIT: Once = Once::new();

pub fn execute() -> String {
    let callid = Uuid::new_v4().to_string();
    println!();
    println!("{} Before V8 call_once: {}", callid, V8_INIT.is_completed());
    V8_INIT.call_once(|| {
        // Initialize V8
        println!("{} Beginning V8 init", callid);
        let platform = v8::new_default_platform(0, false).make_shared();
        v8::V8::initialize_platform(platform);
        v8::V8::initialize();
        println!("{} Completed V8 init", callid);
    });
    println!("{} After V8 call_once: {}", callid, V8_INIT.is_completed());

    // Create a new Isolate and make it the current one.
    let isolate = &mut v8::Isolate::new(v8::CreateParams::default());

    // Create a stack-allocated handle scope.
    let scope = &mut v8::HandleScope::new(isolate);
    let context = v8::Context::new(scope);
    let scope = &mut v8::ContextScope::new(scope, context);

    let code = String::from("(1 + 1).toString()");

    // Compile and run the source code
    let v8_code = v8::String::new(scope, &code).unwrap();
    let script = v8::Script::compile(scope, v8_code, None).unwrap();
    let value = script.run(scope).unwrap();

    // Extract and return the results
    let result = value.to_string(scope);
    result.unwrap().to_rust_string_lossy(scope)
}


#[cfg(test)]
mod tests {
    use crate::execute;

    #[cfg(not(feature = "individual"))]
    #[test]
    fn test_attempt_combined() {
        assert_eq!(execute(), "2");
        assert_eq!(execute(), "2");
    }

    #[cfg(feature = "individual")]
    #[test]
    fn test_attempt_1() {
        assert_eq!(execute(), "2");
    }

    #[cfg(feature = "individual")]
    #[test]
    fn test_attempt_2() {
        assert_eq!(execute(), "2");
    }
}

Cargo.toml

[package]
name = "v8-demo"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
uuid = { version = "1.6.1", features = ["v4"] }
v8 = "0.82.0"

[features]
individual=[]

Output (two V8 calls in same test function)

$ cargo test
    Finished test [unoptimized + debuginfo] target(s) in 0.00s
     Running unittests src/lib.rs (target/debug/deps/v8_demo-b0467449950f74d0)

running 1 test
test tests::test_attempt_combined ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests v8-demo

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Output (two test functions calling V8 once)

$ cargo test
    Finished test [unoptimized + debuginfo] target(s) in 0.00s
     Running unittests src/lib.rs (target/debug/deps/v8_demo-b0467449950f74d0)

running 1 test
test tests::test_attempt_combined ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests v8-demo

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Output (two unit test functions calling V8 once)

$ cargo test -F individual
    Finished test [unoptimized + debuginfo] target(s) in 0.00s
     Running unittests src/lib.rs (target/debug/deps/v8_demo-baa9331bf38bd06c)

running 2 tests
error: test failed, to rerun pass `--lib`

Caused by:
  process didn't exit successfully: `/mnt/data/projects/Personal/apicize/rust/@apicize/v8-demo/target/debug/deps/v8_demo-baa9331bf38bd06c` (signal: 11, SIGSEGV: invalid memory reference)

Output (two unit test functions calling V8 once, single thread)

$ cargo test -F individual -- --test-threads=1
    Finished test [unoptimized + debuginfo] target(s) in 0.00s
     Running unittests src/lib.rs (target/debug/deps/v8_demo-baa9331bf38bd06c)

running 2 tests
test tests::test_attempt_1 ... ok
test tests::test_attempt_2 ... error: test failed, to rerun pass `--lib`

Caused by:
  process didn't exit successfully: `/mnt/data/projects/Personal/apicize/rust/@apicize/v8-demo/target/debug/deps/v8_demo-baa9331bf38bd06c --test-threads=1` (signal: 11, SIGSEGV: invalid memory reference)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions