Skip to content

Rust's newly added assert_unsafe_precondition makes GDNative .dlls that are compiled in debug mode crash on startup #1077

Open
@Houtamelo

Description

@Houtamelo

Minimal reproducible project:
GDNative - Bug Reproduction.zip

Compiled using Rust version 1.79.0-nightly (8b2459c1f 2024-04-09).

Godot version: 3.5.3
Platform: Windows 10 64 bit

How to Reproduce

  1. Compile the rust project in debug mode. (run cargo build in the directory "rust")
  2. Replace the .dll file in the godot project (path: "godot\Bin\gdnative_minimal_bug_reproduction.dll") with the generated .dll file (path: "target/debug/gdnative_minimal_bug_reproduction.dll").
  3. Open the project using Godot's editor.
  4. Run the scene "Node.tscn"
  5. The game will crash during startup, without printing "Hello World" in the console.

Steps 1 and 2 can also be done by running "build_and_move_debug.bat"


Since assert_unsafe_precondition is stripped in release mode, this bug only happens in debug mode.
This can be proven by mimicking the reproduction steps but compiling in release mode:

  1. Compile the rust project in release mode. (run cargo build --release in the directory "rust")
  2. Replace the .dll file in the godot project (path: "godot\Bin\gdnative_minimal_bug_reproduction.dll") with the generated .dll file (path: "target/release/gdnative_minimal_bug_reproduction.dll").
  3. Open the project using Godot's editor.
  4. Run the scene "Node.tscn"
  5. The game will run normally, printing "Hello World" in the console.

Steps 1 and 2 can also be done by running "build_and_move_release.bat"


Additional info

When using the panic handler recipe in the book, it manages to catch a stack trace of the first error:

E 0:00:00.501   <unset>: [RUST] file 'library\core\src\panicking.rs' at line 215: panic occurred: "unsafe precondition(s) violated: slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`"
  <C++ Source>  rust\src\lib.rs:67 @ <unset>()

This stack trace was gathered on another project though, here's the script:

fn init_panic_hook() {
    let old_hook = std::panic::take_hook();
    std::panic::set_hook(Box::new(move |panic_info| {
        let loc_string;
        if let Some(location) = panic_info.location() {
            loc_string = format!("file '{}' at line {}", location.file(), location.line());
        } else {
            loc_string = own!("unknown location")
        }

        let error_message;
        if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
            error_message = format!("[RUST] {}: panic occurred: {:?}", loc_string, s);
        } else if let Some(s) = panic_info.payload().downcast_ref::<String>() {
            error_message = format!("[RUST] {}: panic occurred: {:?}", loc_string, s);
        } else {
            error_message = format!("[RUST] {}: unknown panic occurred", loc_string);
        }
 line 67 ->>>    godot_error!("{}", error_message);  <<<- line 67
        (*(old_hook.as_ref()))(panic_info);

        unsafe {
            if let Some(gd_panic_hook) = autoload::<Node>("rust_panic_hook") {
                gd_panic_hook.call("rust_panic_hook", &[GodotString::from_str(error_message).to_variant()]);
            }
        }
    }));
}

Current workaround is to simply not use debug mode, but that disables some useful assertions made by GDNative that can catch many UB cases.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions