From ebaa878dcee487cb70483a25fad0a03eeccac2aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 23 Jun 2026 17:20:45 +0200 Subject: [PATCH] notify: fix crash in notify_done() when terminal has been closed before notification In server mode, if a terminal instance triggered a tracked notification (e.g. a bell notification, or a kitty notification), then the foot server crashed if the terminal instance was closed before the notification. This happened because the notify_done() callback's user supplied argument was the terminal instance, and this pointer was not verified before it was de-referenced. Fix by searching the wayland global's list of terminal instances, and ignore the callback if the wanted terminal instance is gone. Closes #2397 --- CHANGELOG.md | 4 ++++ notify.c | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64b1ac07..c611f5ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -114,6 +114,9 @@ ([#2364][2364]). * Escape quotes in file names being DnD:ed on the command line ([#2363][2363]). +* Crash in `--server` mode, when a tracked notification is closed + after the associated terminal instance has been closed + ([#2397][2397]). [2353]: https://codeberg.org/dnkl/foot/issues/2353 [2352]: https://codeberg.org/dnkl/foot/issues/2352 @@ -122,6 +125,7 @@ [2360]: https://codeberg.org/dnkl/foot/issues/2360 [2370]: https://codeberg.org/dnkl/foot/issues/2370 [2364]: https://codeberg.org/dnkl/foot/issues/2364 +[2397]: https://codeberg.org/dnkl/foot/issues/2397 ### Security diff --git a/notify.c b/notify.c index e454b03b..846c7035 100644 --- a/notify.c +++ b/notify.c @@ -233,10 +233,31 @@ fdm_notify_stdout(struct fdm *fdm, int fd, int events, void *data) return true; } +struct done_context { + struct wayland *wayl; + struct terminal *term; +}; + static void notif_done(struct reaper *reaper, pid_t pid, int status, void *data) { - struct terminal *term = data; + struct done_context *ctx = data; + const struct wayland *wayl = ctx->wayl; + const struct terminal *wanted_term = ctx->term; + free(ctx); + + struct terminal *term = NULL; + tll_foreach(wayl->terms, it) { + if (it->item == wanted_term) { + term = it->item; + break; + } + } + + if (term == NULL) { + LOG_WARN("notification closed, but the associated terminal instance is already gone"); + return; + } tll_foreach(term->active_notifications, it) { struct notification *notif = &it->item; @@ -567,11 +588,18 @@ notify_notify(struct terminal *term, struct notification *notif) &fdm_notify_stdout, (void *)term); } + struct done_context *ctx = NULL; + if (track_notification) { + ctx = xmalloc(sizeof(*ctx)); + ctx->wayl = term->wl; + ctx->term = term; + } + /* Redirect stdin to /dev/null, but ignore failure to open */ int devnull = open("/dev/null", O_RDONLY); pid_t pid = spawn( term->reaper, NULL, argv, devnull, stdout_fds[1], -1, - track_notification ? ¬if_done : NULL, (void *)term, NULL); + track_notification ? ¬if_done : NULL, ctx, NULL); if (stdout_fds[1] >= 0) { /* Close write-end of stdout pipe */ @@ -649,9 +677,7 @@ notify_close(struct terminal *term, const char *id) } int devnull = open("/dev/null", O_RDONLY); - spawn( - term->reaper, NULL, argv, devnull, -1, -1, - NULL, (void *)term, NULL); + spawn(term->reaper, NULL, argv, devnull, -1, -1, NULL, NULL, NULL); if (devnull >= 0) close(devnull);