Spend a few more tokens to fix this. Note that I have not carefully verified the patch, but it seems to fix both of the segfaults I encountered (but still does not make resume work, that is a problem on the server side). I also encountered an infinite loop (100% CPU usage while trying in vain to start the proxy), but did not generate a patch for that yet, let me know if you want one. Cheers, Philipp -------------------------------------------------------------------- NOTE ON HOW THIS FOLLOW-UP WAS PRODUCED -------------------------------------------------------------------- Same as the original report: debugging and patch drafting done together with an LLM assistant (Claude) at my direction. I built, ran, and verified everything myself. Flagging this again per the original subject tag. -------------------------------------------------------------------- SUMMARY -------------------------------------------------------------------- Root cause found and fixed. The crash was NOT a libssh bug or a generic race -- it was a x2goclient-side logic bug in SshMasterConnection::createChannelConnection() / channelLoop(), specifically a dangling-pointer bug in the array x2goclient hands to ssh_select(). Two distinct issues in that area, both patched below. I've been running the patched build since and no longer see this crash, including in a case that previously crashed within seconds of "resuming normal session". -------------------------------------------------------------------- ROOT CAUSE #1: stale channel pointer not cleared on error -------------------------------------------------------------------- In createChannelConnection() (src/sshmasterconnection.cpp), when ssh_channel_open_session() or ssh_channel_request_exec() fails after a channel has already been stored via channelConnections[i].channel = channel; the error paths do: ssh_channel_free (channel); ... return (false); without resetting channelConnections[i].channel back to 0. The caller in channelLoop() just does "continue;" on failure, so the stale entry survives. On the *next* pass, createChannelConnection() checks: if ( channelConnections.at ( i ).channel==0l ) which is false (the pointer is non-null, just freed), so the "create a new channel" branch is skipped entirely, and the dangling pointer is handed straight to ssh_select() via read_chan[i]. Confirmed with valgrind: an ssh_channel allocated and freed with both call sites inside channelLoop() itself, then read again on the very next ssh_select() pass. Fix: explicitly reset channelConnections[i].channel = 0l on both error paths, right after ssh_channel_free(). -------------------------------------------------------------------- ROOT CAUSE #2: uninitialized array slot on same-iteration failure -------------------------------------------------------------------- Separately, in channelLoop(): ssh_channel* read_chan=new ssh_channel[channelConnections.size() +1]; ssh_channel* out_chan=new ssh_channel[channelConnections.size() +1]; read_chan[channelConnections.size() ]=NULL; new T[n] does not zero-initialize a plain pointer array. For any index whose createChannelConnection() call fails and returns early (before reaching the "read_chan[i] = ..." line at the end of that function), read_chan[i] is left as raw heap garbage, not NULL -- so ssh_select()'s NULL-terminated scan of the array walks straight into it. This one can crash within a single channelLoop() iteration, with no prior history needed, which is what I saw when a channel failed immediately on session resume. Fix: value-initialize both arrays ("new ssh_channel[n]()") so every untouched slot starts NULL rather than garbage. -------------------------------------------------------------------- PATCH -------------------------------------------------------------------- Both fixes, against the 4.1.2.3 source tree (attached in full as x2goclient-dangling-channel-v2.patch; apply with "patch -p0" from the top of the source tree): --- src/sshmasterconnection.cpp.orig +++ src/sshmasterconnection.cpp @@ -2205,6 +2205,11 @@ /* Free channel. */ ssh_channel_free (channel); + /* Local patch: without this, channelConnections[i].channel + * keeps pointing at freed memory, and the next loop + * iteration's "channel==0l" check wrongly treats it as + * still valid, handing a dangling pointer straight to + * ssh_select(). */ + channelConnections[i].channel = 0l; emit ioErr ( channelConnections[i].creator, errorMsg, err ); x2goDebug<