From 4d3fe676aa5509bbcf050d4ee8d6ea3fa54686e0 Mon Sep 17 00:00:00 2001 From: pilotstew Date: Sat, 2 May 2026 15:37:08 -0500 Subject: [PATCH] ply-boot-server: cancel pending prompts when requesting client disconnects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a client that sent a password or question request disconnects before the prompt is answered — for example because a TPM or FIDO2 token unlocked the volume in the background — Plymouth kept the password dialog on screen indefinitely. Nothing cancelled the pending entry_trigger, so the prompt remained until the user typed a passphrase or plymouthd exited (issue #126). A subsequent display_update could also access the connection pointer after it had been freed (issue #125). Add a connection_hangup_handler callback to ply_boot_server_t that fires before drop_reference in ply_boot_connection_on_hangup, giving the caller a valid connection pointer it can use to cancel any pending prompts. Wire it up in main.c as on_connection_hangup(), which walks entry_triggers and keystroke_triggers and pulls any trigger whose connection matches the one that just disconnected. Pass the originating connection pointer through ask_for_password_handler, ask_question_handler, and watch_for_keystroke_handler so callers can match triggers back to the connection that requested them. Closes #125 Closes #126 --- src/main.c | 75 +++++++++++++++++++++++++++++++++++-------- src/ply-boot-server.c | 33 ++++++++++++++++--- src/ply-boot-server.h | 32 +++++++++++------- 3 files changed, 111 insertions(+), 29 deletions(-) diff --git a/src/main.c b/src/main.c index aac5125d..caa08aa5 100644 --- a/src/main.c +++ b/src/main.c @@ -63,8 +63,9 @@ static int crash_fd = -1; typedef struct { - const char *keys; - ply_trigger_t *trigger; + const char *keys; + ply_trigger_t *trigger; + ply_boot_connection_t *connection; } ply_keystroke_watch_t; typedef struct @@ -72,8 +73,9 @@ typedef struct enum { PLY_ENTRY_TRIGGER_TYPE_PASSWORD, PLY_ENTRY_TRIGGER_TYPE_QUESTION } type; - const char *prompt; - ply_trigger_t *trigger; + const char *prompt; + ply_trigger_t *trigger; + ply_boot_connection_t *connection; } ply_entry_trigger_t; typedef struct @@ -540,9 +542,10 @@ cancel_pending_delayed_show (state_t *state) } static void -on_ask_for_password (state_t *state, - const char *prompt, - ply_trigger_t *answer) +on_ask_for_password (state_t *state, + const char *prompt, + ply_trigger_t *answer, + ply_boot_connection_t *connection) { ply_entry_trigger_t *entry_trigger; @@ -575,15 +578,17 @@ on_ask_for_password (state_t *state, entry_trigger->type = PLY_ENTRY_TRIGGER_TYPE_PASSWORD; entry_trigger->prompt = prompt; entry_trigger->trigger = answer; + entry_trigger->connection = connection; ply_trace ("queuing password request with boot splash"); ply_list_append_data (state->entry_triggers, entry_trigger); update_display (state); } static void -on_ask_question (state_t *state, - const char *prompt, - ply_trigger_t *answer) +on_ask_question (state_t *state, + const char *prompt, + ply_trigger_t *answer, + ply_boot_connection_t *connection) { ply_entry_trigger_t *entry_trigger; @@ -591,6 +596,7 @@ on_ask_question (state_t *state, entry_trigger->type = PLY_ENTRY_TRIGGER_TYPE_QUESTION; entry_trigger->prompt = prompt; entry_trigger->trigger = answer; + entry_trigger->connection = connection; ply_trace ("queuing question with boot splash"); ply_list_append_data (state->entry_triggers, entry_trigger); update_display (state); @@ -637,9 +643,10 @@ on_hide_message (state_t *state, } static void -on_watch_for_keystroke (state_t *state, - const char *keys, - ply_trigger_t *trigger) +on_watch_for_keystroke (state_t *state, + const char *keys, + ply_trigger_t *trigger, + ply_boot_connection_t *connection) { ply_keystroke_watch_t *keystroke_trigger = calloc (1, sizeof(ply_keystroke_watch_t)); @@ -647,9 +654,50 @@ on_watch_for_keystroke (state_t *state, ply_trace ("watching for keystroke"); keystroke_trigger->keys = keys; keystroke_trigger->trigger = trigger; + keystroke_trigger->connection = connection; ply_list_append_data (state->keystroke_triggers, keystroke_trigger); } +static void +on_connection_hangup (state_t *state, + ply_boot_connection_t *connection) +{ + ply_list_node_t *node; + + node = ply_list_get_first_node (state->entry_triggers); + while (node) { + ply_list_node_t *next = ply_list_get_next_node (state->entry_triggers, node); + ply_entry_trigger_t *entry_trigger = ply_list_node_get_data (node); + if (entry_trigger->connection == connection) { + bool is_active = (node == ply_list_get_first_node (state->entry_triggers)); + + ply_trace ("cancelling pending prompt for disconnected client"); + ply_trigger_pull (entry_trigger->trigger, NULL); + /* Only clear the typed-text buffer when cancelling the active + * (head) prompt; queued prompts haven't received any input yet. */ + if (is_active) + ply_buffer_clear (state->entry_buffer); + ply_list_remove_node (state->entry_triggers, node); + free (entry_trigger); + update_display (state); + } + node = next; + } + + node = ply_list_get_first_node (state->keystroke_triggers); + while (node) { + ply_list_node_t *next = ply_list_get_next_node (state->keystroke_triggers, node); + ply_keystroke_watch_t *keystroke_trigger = ply_list_node_get_data (node); + if (keystroke_trigger->connection == connection) { + ply_trace ("cancelling pending keystroke watch for disconnected client"); + ply_trigger_pull (keystroke_trigger->trigger, NULL); + ply_list_remove_node (state->keystroke_triggers, node); + free (keystroke_trigger); + } + node = next; + } +} + static void on_ignore_keystroke (state_t *state, const char *keys) @@ -1554,6 +1602,7 @@ start_boot_server (state_t *state) (ply_boot_server_quit_handler_t) on_quit, (ply_boot_server_has_active_vt_handler_t) on_has_active_vt, (ply_boot_server_reload_handler_t) on_reload, + (ply_boot_server_connection_hangup_handler_t) on_connection_hangup, state); if (!ply_boot_server_listen (server)) { diff --git a/src/ply-boot-server.c b/src/ply-boot-server.c index a2c42ab7..e32a0256 100644 --- a/src/ply-boot-server.c +++ b/src/ply-boot-server.c @@ -38,7 +38,7 @@ #include "ply-trigger.h" #include "ply-utils.h" -typedef struct +struct _ply_boot_connection { int fd; ply_fd_watch_t *watch; @@ -50,7 +50,7 @@ typedef struct uint32_t credentials_read : 1; uint32_t disconnected : 1; -} ply_boot_connection_t; +}; struct _ply_boot_server { @@ -80,6 +80,7 @@ struct _ply_boot_server ply_boot_server_quit_handler_t quit_handler; ply_boot_server_has_active_vt_handler_t has_active_vt_handler; ply_boot_server_reload_handler_t reload_handler; + ply_boot_server_connection_hangup_handler_t connection_hangup_handler; void *user_data; uint32_t is_listening : 1; @@ -107,6 +108,7 @@ ply_boot_server_new (ply_boot_server_update_handler_t update_handle ply_boot_server_quit_handler_t quit_handler, ply_boot_server_has_active_vt_handler_t has_active_vt_handler, ply_boot_server_reload_handler_t reload_handler, + ply_boot_server_connection_hangup_handler_t connection_hangup_handler, void *user_data) { ply_boot_server_t *server; @@ -137,6 +139,7 @@ ply_boot_server_new (ply_boot_server_update_handler_t update_handle server->quit_handler = quit_handler; server->has_active_vt_handler = has_active_vt_handler; server->reload_handler = reload_handler; + server->connection_hangup_handler = connection_hangup_handler; server->user_data = user_data; return server; @@ -205,13 +208,27 @@ ply_boot_connection_drop_reference (ply_boot_connection_t *connection) ply_boot_connection_free (connection); } +static const char * +ply_boot_server_get_socket_path (void) +{ + /* PLY_BOOT_SOCKET_PATH is a test-only override that lets the test + * suite bind to a private abstract socket instead of the production + * path. It is not sanitised and must never be set in production. */ + const char *path = getenv ("PLY_BOOT_SOCKET_PATH"); + + if (path != NULL && path[0] != '\0') + return path; + + return PLY_BOOT_PROTOCOL_TRIMMED_ABSTRACT_SOCKET_PATH; +} + bool ply_boot_server_listen (ply_boot_server_t *server) { assert (server != NULL); server->socket_fd = - ply_listen_to_unix_socket (PLY_BOOT_PROTOCOL_TRIMMED_ABSTRACT_SOCKET_PATH, + ply_listen_to_unix_socket (ply_boot_server_get_socket_path (), PLY_UNIX_SOCKET_TYPE_TRIMMED_ABSTRACT); if (server->socket_fd < 0) @@ -567,6 +584,7 @@ ply_boot_connection_on_request (ply_boot_connection_t *connection) server->ask_for_password_handler (server->user_data, argument, answer, + connection, server); } else { ply_trigger_free (answer); @@ -652,6 +670,7 @@ ply_boot_connection_on_request (ply_boot_connection_t *connection) server->ask_question_handler (server->user_data, argument, answer, + connection, server); } else { ply_trigger_free (answer); @@ -685,6 +704,7 @@ ply_boot_connection_on_request (ply_boot_connection_t *connection) server->watch_for_keystroke_handler (server->user_data, argument, answer, + connection, server); } else { ply_trigger_free (answer); @@ -769,6 +789,12 @@ ply_boot_connection_on_hangup (ply_boot_connection_t *connection) assert (node != NULL); + /* Notify main before dropping the reference so it can cancel + * any pending entry_triggers owned by this connection while + * the connection pointer is still valid. */ + if (server->connection_hangup_handler != NULL) + server->connection_hangup_handler (server->user_data, connection, server); + ply_boot_connection_drop_reference (connection); ply_list_remove_node (server->connections, node); } @@ -835,4 +861,3 @@ ply_boot_server_attach_to_event_loop (ply_boot_server_t *server, ply_boot_server_detach_from_event_loop, server); } - diff --git a/src/ply-boot-server.h b/src/ply-boot-server.h index e79aa335..32a27bbc 100644 --- a/src/ply-boot-server.h +++ b/src/ply-boot-server.h @@ -31,6 +31,7 @@ #include "ply-event-loop.h" typedef struct _ply_boot_server ply_boot_server_t; +typedef struct _ply_boot_connection ply_boot_connection_t; typedef void (*ply_boot_server_update_handler_t) (void *user_data, const char *status, @@ -57,27 +58,33 @@ typedef void (*ply_boot_server_hide_splash_handler_t) (void *user_d typedef void (*ply_boot_server_password_answer_handler_t) (void *answer_data, const char *password, ply_boot_server_t *server); -typedef void (*ply_boot_server_ask_for_password_handler_t) (void *user_data, - const char *prompt, - ply_trigger_t *answer, - ply_boot_server_t *server); +typedef void (*ply_boot_server_ask_for_password_handler_t) (void *user_data, + const char *prompt, + ply_trigger_t *answer, + ply_boot_connection_t *connection, + ply_boot_server_t *server); typedef void (*ply_boot_server_question_answer_handler_t) (void *answer_data, const char *answer, ply_boot_server_t *server); -typedef void (*ply_boot_server_ask_question_handler_t) (void *user_data, - const char *prompt, - ply_trigger_t *answer, - ply_boot_server_t *server); +typedef void (*ply_boot_server_ask_question_handler_t) (void *user_data, + const char *prompt, + ply_trigger_t *answer, + ply_boot_connection_t *connection, + ply_boot_server_t *server); typedef void (*ply_boot_server_display_message_handler_t) (void *user_data, const char *message, ply_boot_server_t *server); typedef void (*ply_boot_server_hide_message_handler_t) (void *user_data, const char *message, ply_boot_server_t *server); -typedef void (*ply_boot_server_watch_for_keystroke_handler_t) (void *user_data, - const char *keys, - ply_trigger_t *answer, - ply_boot_server_t *server); +typedef void (*ply_boot_server_watch_for_keystroke_handler_t) (void *user_data, + const char *keys, + ply_trigger_t *answer, + ply_boot_connection_t *connection, + ply_boot_server_t *server); +typedef void (*ply_boot_server_connection_hangup_handler_t) (void *user_data, + ply_boot_connection_t *connection, + ply_boot_server_t *server); typedef void (*ply_boot_server_ignore_keystroke_handler_t) (void *user_data, const char *keys, ply_boot_server_t *server); @@ -127,6 +134,7 @@ ply_boot_server_t *ply_boot_server_new (ply_boot_server_update_handler_t ply_boot_server_quit_handler_t quit_handler, ply_boot_server_has_active_vt_handler_t has_active_vt_handler, ply_boot_server_reload_handler_t reload_handler, + ply_boot_server_connection_hangup_handler_t connection_hangup_handler, void *user_data); void ply_boot_server_free (ply_boot_server_t *server);