Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

free can be used to cause a segfault entirely from rust #348

Closed
lilizoey opened this issue Jul 18, 2023 · 4 comments · Fixed by #417
Closed

free can be used to cause a segfault entirely from rust #348

lilizoey opened this issue Jul 18, 2023 · 4 comments · Fixed by #417
Labels
bug c: engine Godot classes (nodes, resources, ...) ub Undefined behavior

Comments

@lilizoey
Copy link
Member

use godot::{engine::Engine, prelude::*};
struct Testing;

#[gdextension]
unsafe impl ExtensionLibrary for Testing {}

#[derive(GodotClass)]
#[class(init, base = Node)]
pub struct Foo {
    #[base]
    base: Base<Node>,
}

#[godot_api]
impl NodeVirtual for Foo {
    fn ready(&mut self) {
        if Engine::singleton().is_editor_hint() {
            return;
        }

        let node = Node::new_alloc();
        let node2 = node.share();
        node2.free();

        self.base.add_child(node);
    }
}

Adding a node of class Foo will cause a segfault when the game runs.

@lilizoey lilizoey added bug c: engine Godot classes (nodes, resources, ...) labels Jul 18, 2023
@Bromeon Bromeon added the ub Undefined behavior label Jul 18, 2023
@lilizoey
Copy link
Member Author

Another related pattern that is harder to guard against:

let child = Node::new_alloc();
let mut node = Node::new_alloc();
let node2 = node.share();

let node_deref = &mut *node;
node2.free();
// segfaults
node_deref.add_child(child.share());
child.free();

@lilizoey
Copy link
Member Author

The most straightforward solution here would be to make deref check for liveness (as a best effort), and to make free an unsafe function. Hopefully people can mostly use reference counting and queue_free for managing this. But if people use free a lot then this would add a bunch of unsafe to the code.

But again, objects that dont inherit from Node and RefCounted are kinda rare in general. We have a bunch of them in our integration tests, but i dont think that's a representative sample.

I'm not sure if there's a good way to make a completely safe abstraction for this.

@lilizoey
Copy link
Member Author

Some conversation about this issue happening in this discord thread.

@lilizoey
Copy link
Member Author

queue_free can also segfault from rust:

#[derive(GodotClass)]
#[class(init, base = Node)]
pub struct Foo {
    #[base]
    base: Base<Node>,
    node: Option<&'static mut Node>,
    i: u64,
}

#[godot_api]
impl NodeVirtual for Foo {
    fn ready(&mut self) {
        if Engine::singleton().is_editor_hint() {
            return;
        }

        let node = Node::new_alloc();

        self.add_child(node.share());

        self.node = Some(&mut *Box::leak(Box::new(node)));
    }

    fn process(&mut self, _delta: f64) {
        let i = self.i;
        if let Some(node) = &mut self.node {
            match i {
                0 => node.queue_free(),
                1 => {
                    let child = Node::new_alloc();
                    node.add_child(child);
                }
                _ => (),
            }
        }

        self.i += 1;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug c: engine Godot classes (nodes, resources, ...) ub Undefined behavior
Projects
None yet
2 participants