1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-08 08:58:00 +00:00

Fix premature exit if 'plink -shareexists' happens early.

A user reported a phenomenon where running 'plink -shareexists' very
early in the connection would cause the receiving upstream PuTTY to
exit cleanly with the message 'All channels closed' in the log.

That wasn't hard to track down: that happens as a result of the
connection layer callback sharing_no_more_downstreams(), which causes
the connection layer to check whether it has any channels left open,
and if not, to terminate the connection on the grounds that everything
has finished. But it's premature to draw that conclusion if the reason
no channels are open if we haven't _started_ yet! Now we have a
'started' flag which is set when we initialise mainchan, and the
'we're all done now' check will never fire before that flag is set.

But in the course of investigating that, I found a second problem in
the same area: at an even earlier stage of an SSH connection, the
connshare system doesn't _even_ have the real ConnectionLayer pointer
yet. Instead, it has a pointer to a dummy one provided by the
top-level ssh.c, which contains a NULL vtable pointer. So if it calls
sharing_no_more_downstreams on _that_ ConnectionLayer, it will
dereference NULL and crash. So I've filled in cl_dummy's vtable
pointer with a trivial vtable, containing only the one callback
sharing_no_more_downstreams, which itself is a no-op function.

Hopefully that should all be stable now.
This commit is contained in:
Simon Tatham 2021-02-21 10:11:13 +00:00
parent f47e351cee
commit a859955689
3 changed files with 19 additions and 0 deletions

8
ssh.c
View File

@ -865,6 +865,13 @@ bool ssh_is_bare(Ssh *ssh)
return ssh->backend.vt->protocol == PROT_SSHCONN;
}
/* Dummy connlayer must provide ssh_sharing_no_more_downstreams,
* because it might be called early due to plink -shareexists */
static void dummy_sharing_no_more_downstreams(ConnectionLayer *cl) {}
static const ConnectionLayerVtable dummy_connlayer_vtable = {
.sharing_no_more_downstreams = dummy_sharing_no_more_downstreams,
};
/*
* Called to set up the connection.
*
@ -900,6 +907,7 @@ static char *ssh_init(const BackendVtable *vt, Seat *seat,
ssh->bare_connection = (vt->protocol == PROT_SSHCONN);
ssh->seat = seat;
ssh->cl_dummy.vt = &dummy_connlayer_vtable;
ssh->cl_dummy.logctx = ssh->logctx = logctx;
random_ref(); /* do this now - may be needed by sharing setup code */

View File

@ -1020,6 +1020,7 @@ static void ssh2_connection_process_queue(PacketProtocolLayer *ppl)
s->mainchan = mainchan_new(
&s->ppl, &s->cl, s->conf, s->term_width, s->term_height,
s->ssh_is_simple, &s->mainchan_sc);
s->started = true;
/*
* Transfer data!
@ -1248,6 +1249,15 @@ static void ssh2_check_termination(struct ssh2_connection_state *s)
if (s->persistent)
return; /* persistent mode: never proactively terminate */
if (!s->started) {
/* At startup, we don't have any channels open because we
* haven't got round to opening the main one yet. In that
* situation, we don't want to terminate, even if a sharing
* connection opens and closes and causes a call to this
* function. */
return;
}
if (count234(s->channels) == 0 &&
!(s->connshare && share_ndownstreams(s->connshare) > 0)) {
/*

View File

@ -19,6 +19,7 @@ struct ssh2_connection_state {
bool ssh_is_simple;
bool persistent;
bool started;
Conf *conf;