gtk: fix crash due to accessing invalidated pointer to adwaita notebook (#4926)

#4235 introduced a crash when you closed the last tab in a window. In
`NotebookAdw.closeTab` a `defer` was added that references `self`. If
the last tab is closed we destroy the window. As part of that process
`self` becomes invalid because the window has been de-initialized. The
`defer` fires at the end of the function, referencing the invalid
pointer and causing a crash.

```
info(surface): surface closed addr=7fffe400a000
debug(gtk): window destroy

(process:1032400): Gtk-CRITICAL **: 18:40:17.674: gtk_widget_unparent: assertion 'GTK_IS_WIDGET (widget)' failed
Segmentation fault at address 0x7ffff4dad040
/home/jeff/dev/ghostty/src/apprt/gtk/notebook_adw.zig:128:19: 0x340226a in closeTab (ghostty)
        defer self.forcing_close = false;
                  ^
/home/jeff/dev/ghostty/src/apprt/gtk/notebook.zig:157:40: 0x336ca86 in closeTab (ghostty)
            .adw => |*adw| adw.closeTab(tab),
                                       ^
/home/jeff/dev/ghostty/src/apprt/gtk/Window.zig:468:27: 0x327628d in closeTab (ghostty)
    self.notebook.closeTab(tab);
                          ^
/home/jeff/dev/ghostty/src/apprt/gtk/Tab.zig:121:25: 0x336581b in remove (ghostty)
    self.window.closeTab(self);
                        ^
/home/jeff/dev/ghostty/src/apprt/gtk/Surface.zig:207:34: 0x326a213 in remove (ghostty)
            .tab_ => |t| t.remove(),
                                 ^
/home/jeff/dev/ghostty/src/apprt/gtk/Surface.zig:722:30: 0x31e3a3a in close (ghostty)
        self.container.remove();
                             ^
/home/jeff/dev/ghostty/src/Surface.zig:733:26: 0x31e1dc4 in close (ghostty)
    self.rt_surface.close(self.needsConfirmQuit());
                         ^
/home/jeff/dev/ghostty/src/Surface.zig:925:23: 0x31e143e in handleMessage (ghostty)
            self.close();
                      ^
/home/jeff/dev/ghostty/src/App.zig:486:34: 0x31e2d05 in surfaceMessage (ghostty)
        try surface.handleMessage(msg);
                                 ^
/home/jeff/dev/ghostty/src/App.zig:252:62: 0x31e3005 in drainMailbox (ghostty)
            .surface_message => |msg| try self.surfaceMessage(msg.surface, msg.message),
                                                             ^
/home/jeff/dev/ghostty/src/App.zig:138:26: 0x31e378e in tick (ghostty)
    try self.drainMailbox(rt_app);
                         ^
/home/jeff/dev/ghostty/src/apprt/gtk/App.zig:1279:31: 0x31e3e42 in run (ghostty)
        try self.core_app.tick(self);
                              ^
/home/jeff/dev/ghostty/src/main_ghostty.zig:112:24: 0x31e52f4 in main (ghostty)
    try app_runtime.run();
                       ^
/nix/store/h6lccra69nr23676qq32h9qn1fba24v1-zig-0.13.0/lib/std/start.zig:524:37: 0x31e5e0e in main (ghostty)
            const result = root.main() catch |err| {
                                    ^
???:?:?: 0x7ffff682a1fb in ??? (libc.so.6)
Unwind information for `libc.so.6:0x7ffff682a1fb` was not available, trace may be incomplete

fish: Job 2, './zig-out/bin/ghostty --config-…' terminated by signal SIGABRT (Abort)
```
This commit is contained in:
Mitchell Hashimoto
2025-01-10 17:31:24 -08:00
committed by GitHub

View File

@ -125,7 +125,12 @@ pub const NotebookAdw = struct {
// as true so that the close_page call below doesn't request
// confirmation.
self.forcing_close = true;
defer self.forcing_close = false;
const n = self.nPages();
defer {
// self becomes invalid if we close the last page because we close
// the whole window
if (n > 1) self.forcing_close = false;
}
const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box)) orelse return;
c.adw_tab_view_close_page(self.tab_view, page);
@ -143,6 +148,8 @@ pub const NotebookAdw = struct {
c.g_object_unref(tab.box);
}
// `self` will become invalid after this call because it will have
// been freed up as part of the process of closing the window.
c.gtk_window_destroy(tab.window.window);
}
}