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
This commit is contained in:
Daniel Eklöf 2026-06-23 17:20:45 +02:00
parent 73e6d21000
commit ebaa878dce
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
2 changed files with 35 additions and 5 deletions

View file

@ -114,6 +114,9 @@
([#2364][2364]). ([#2364][2364]).
* Escape quotes in file names being DnD:ed on the command line * Escape quotes in file names being DnD:ed on the command line
([#2363][2363]). ([#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 [2353]: https://codeberg.org/dnkl/foot/issues/2353
[2352]: https://codeberg.org/dnkl/foot/issues/2352 [2352]: https://codeberg.org/dnkl/foot/issues/2352
@ -122,6 +125,7 @@
[2360]: https://codeberg.org/dnkl/foot/issues/2360 [2360]: https://codeberg.org/dnkl/foot/issues/2360
[2370]: https://codeberg.org/dnkl/foot/issues/2370 [2370]: https://codeberg.org/dnkl/foot/issues/2370
[2364]: https://codeberg.org/dnkl/foot/issues/2364 [2364]: https://codeberg.org/dnkl/foot/issues/2364
[2397]: https://codeberg.org/dnkl/foot/issues/2397
### Security ### Security

View file

@ -233,10 +233,31 @@ fdm_notify_stdout(struct fdm *fdm, int fd, int events, void *data)
return true; return true;
} }
struct done_context {
struct wayland *wayl;
struct terminal *term;
};
static void static void
notif_done(struct reaper *reaper, pid_t pid, int status, void *data) 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) { tll_foreach(term->active_notifications, it) {
struct notification *notif = &it->item; struct notification *notif = &it->item;
@ -567,11 +588,18 @@ notify_notify(struct terminal *term, struct notification *notif)
&fdm_notify_stdout, (void *)term); &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 */ /* Redirect stdin to /dev/null, but ignore failure to open */
int devnull = open("/dev/null", O_RDONLY); int devnull = open("/dev/null", O_RDONLY);
pid_t pid = spawn( pid_t pid = spawn(
term->reaper, NULL, argv, devnull, stdout_fds[1], -1, term->reaper, NULL, argv, devnull, stdout_fds[1], -1,
track_notification ? &notif_done : NULL, (void *)term, NULL); track_notification ? &notif_done : NULL, ctx, NULL);
if (stdout_fds[1] >= 0) { if (stdout_fds[1] >= 0) {
/* Close write-end of stdout pipe */ /* 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); int devnull = open("/dev/null", O_RDONLY);
spawn( spawn(term->reaper, NULL, argv, devnull, -1, -1, NULL, NULL, NULL);
term->reaper, NULL, argv, devnull, -1, -1,
NULL, (void *)term, NULL);
if (devnull >= 0) if (devnull >= 0)
close(devnull); close(devnull);