From 15c98a73336c22dc8c35d7ef37b59a01a726edb1 Mon Sep 17 00:00:00 2001 From: JamesWrigley Date: Tue, 24 Sep 2024 11:00:29 +0200 Subject: [PATCH 1/2] Always close all unclaimed channels of a Client --- src/server.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/server.jl b/src/server.jl index 0f856e6..14708d0 100644 --- a/src/server.jl +++ b/src/server.jl @@ -696,6 +696,10 @@ function Base.close(client::Client) close(op) end + for sshchan in client.unclaimed_channels + close(sshchan) + end + close(client.session_event) close(client.session) wait(client.task) From 55ef5e4bb35acf6230d0aba4002a7aeb8a704611 Mon Sep 17 00:00:00 2001 From: JamesWrigley Date: Tue, 24 Sep 2024 17:26:19 +0200 Subject: [PATCH 2/2] Handle cases where an SshChannel has been free'd from C If the remote end disconnects it seems that libssh will disconnect/free the `ssh_session`'s channels. This can lead to an `SshChannel` carrying around a pointer to an address that's already been free'd, which leads to segfaults. Now we always check if the session has been disconnected when checking if a `SshChannel` is open and when closing it. --- docs/src/changelog.md | 5 +++++ src/channel.jl | 11 ++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/src/changelog.md b/docs/src/changelog.md index e5a0794..f6204c2 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -15,6 +15,11 @@ Changelog](https://keepachangelog.com). - Add support for passing environment variables to remote commands with [`Base.run(::Cmd)`](@ref) ([#12]). +### Fixed + +- Fixed segfaults that would occur in [`SshChannel`](@ref) when its + [`Session`](@ref) is disconnected by the remote end ([#13]). + ## [v0.5.0] - 2024-08-10 ### Added diff --git a/src/channel.jl b/src/channel.jl index 3d2201c..0d6a1bf 100644 --- a/src/channel.jl +++ b/src/channel.jl @@ -121,7 +121,7 @@ Checks if the channel is open. Wrapper around [`lib.ssh_channel_is_open()`](@ref). """ function Base.isopen(sshchan::SshChannel) - if isassigned(sshchan) + if isassigned(sshchan) && (isnothing(sshchan.session) || isconnected(sshchan.session)) lib.ssh_channel_is_open(sshchan.ptr) != 0 else false @@ -160,8 +160,13 @@ function Base.close(sshchan::SshChannel) popat!(sshchan.session.channels, idx) end - # Close the channel - if isopen(sshchan) + if !isnothing(sshchan.session) && !isconnected(sshchan.session) + # If the session has already been disconnected from C + # (e.g. because of the other side disconnecting) then that will + # already have free'd the channel, which means we only need to + # unassign the pointer. + sshchan.ptr = nothing + elseif isopen(sshchan) # This will trigger callbacks closewrite(sshchan)