Skip to content

Object::set_script on owner is UB #182

@ghost

Description

On the Godot side, Object::set_script immediately frees the script instance, causing self to point to garbage for the rest of the function. Any operations on self is consequently UB because of the invalid state. This is problematic because it's expected that owner is an unsafe pointer, but not self, which should be a safe reference.

set_script is incidentally marked unsafe, but only because Object is not reference counted, so it may still come as a surprise to users who call it, because the unsafe keyword may be used so liberally that it no longer makes users alert.

Take this modified hello_world example that showcase the problem:

#[macro_use]
extern crate gdnative;

#[derive(gdnative::NativeClass)]
#[inherit(gdnative::Node)]
struct HelloWorld(i64);

#[gdnative::methods]
impl HelloWorld {
    fn _init(_owner: gdnative::Node) -> Self {
        HelloWorld(42)
    }

    #[export]
    fn _ready(&self, mut owner: gdnative::Node) {
        unsafe {
            owner.set_script(None);
        }
        assert_eq!(42, self.0, "self should not be holding garbage");
        godot_print!("hello, world.")
    }
}

fn init(handle: gdnative::init::InitHandle) {
    handle.add_class::<HelloWorld>();
}

godot_gdnative_init!();
godot_nativescript_init!(init);
godot_gdnative_terminate!();

Output:

thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `42`,
 right: `944####680`: self should not be holding garbage', examples/hello_world/src/lib.rs:19:9

Dealing with the problem

As there are more ways that owner can be freed (along with its script instance), it might be best to replace the userdata Box with an Rc (or even Arc. I'm not sure if it's possible for Godot to free an Object from another thread) to prevent UB even if the Godot side called the destructor midway.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions