From c99261437f91b59eff9eeff4d9ca007d03c20a8d Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Wed, 23 Jul 2025 14:37:45 +0200 Subject: [PATCH] socket: do not free poll object if it is locked As it may a cause a use after free if `send` fails when ssh_poll_ctx_dopoll does its callback ssh_poll_ctx_dopoll still wants to use the poll object later Signed-off-by: Philippe Antoine Reviewed-by: Andreas Schneider --- include/libssh/poll.h | 1 + src/poll.c | 14 ++++++++++++++ src/socket.c | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/libssh/poll.h b/include/libssh/poll.h index 8e30676e..478764b6 100644 --- a/include/libssh/poll.h +++ b/include/libssh/poll.h @@ -157,6 +157,7 @@ void ssh_poll_ctx_free(ssh_poll_ctx ctx); int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p); int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, struct ssh_socket_struct *s); void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p); +bool ssh_poll_is_locked(ssh_poll_handle p); int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout); ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session); int ssh_event_add_poll(ssh_event event, ssh_poll_handle p); diff --git a/src/poll.c b/src/poll.c index ee069293..3e356c49 100644 --- a/src/poll.c +++ b/src/poll.c @@ -682,6 +682,20 @@ void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p) } } +/** + * @brief Returns if a poll object is locked. + * + * @param p Pointer to an already allocated poll object. + * @returns true if the poll object is locked; false otherwise. + */ +bool ssh_poll_is_locked(ssh_poll_handle p) +{ + if (p == NULL) { + return false; + } + return p->lock_cnt > 0; +} + /** * @brief Poll all the sockets associated through a poll object with a * poll context. If any of the events are set after the poll, the diff --git a/src/socket.c b/src/socket.c index a31a4bfe..3e96decc 100644 --- a/src/socket.c +++ b/src/socket.c @@ -481,7 +481,7 @@ void ssh_socket_close(ssh_socket s) #endif } - if (s->poll_handle != NULL) { + if (s->poll_handle != NULL && !ssh_poll_is_locked(s->poll_handle)) { ssh_poll_free(s->poll_handle); s->poll_handle = NULL; }