SIGCHLD race *trivial* patch

mouring at etoh.eviladmin.org mouring at etoh.eviladmin.org
Fri Oct 26 06:25:12 EST 2001


I've not tested this, but have you verified that scp never loses any data?
and the 'ssh site date'  NEVER loses data?

- Ben

On Thu, 25 Oct 2001, Nicolas Williams wrote:

> I forgot to mention the behaviour that this patch fixes: hang-on-exit
> for SSHv2 sessions. See the other "SIGCHLD race condition?" thread for
> more details.
>
> Cheers,
>
> Nico
>
>
> On Thu, Oct 25, 2001 at 03:25:22PM -0400, Nicolas Williams wrote:
> > Yes, this is a patch against an older version of OpenSSH with other
> > stuff anyways, BUT, it's so TRIVIAL(*), that you can see how it would
> > apply to newer versions (which I've not tried).
> >
> > Here's the gist: server_loop2() has a race condition with respect to
> > reception of SIGCHLD and checking/setting child_terminated. This patch
> > does two things: wait_until_can_do_something() adds a 1 second timeout
> > to select() IF AND ONLY IF (!channel_still_open) AND, server_loop2()
> > breaks out of its loop when there are no sessions left.
> >
> > Blocking SIGCHLD before select()ing would not fix the problem, nor would
> > that be very portable.
> >
> > So, summary of changes:
> >
> >  - session.h
> >
> >    Added prototype for session_still_used() boolean function.
> >
> >  - session.c
> >
> >    Added session_still_used() boolean function.
> >
> >  - serverloop.c
> >
> >    Added this bit of code to wait_until_can_do_something():
> >
> >       if (!channel_still_open())
> >               max_time_milliseconds = 1000;
> >
> >    before select()ing.
> >
> >    Added this bit of code to server_loop2():
> >
> >                 if (child_terminated) {
> >                         while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
> >                                 session_close_by_pid(pid, status);
> > -                       child_terminated = 0;
> > +                       if (session_still_used())
> > +                               child_terminated = 0;
> > +                    if (child_terminated && !channel_still_open())
> > +                       break;
> >
> >    so that child_terminated is not reset after handling SIGCHLD *UNLESS*
> >    there are no sessions left open.
> >
> > Also, for server_loop(), perhaps it too should have a check to break
> > out of its loop if the child is terminated and there are no channels
> > still open.
> >
> > (*) I hope :)
> >
> > Cheers,
> >
> > Nico
> >
> >
> > Index: 2_9_p2_w_gss_krb5_named_keys.10/session.h
> > --- 2_9_p2_w_gss_krb5_named_keys.10/session.h Tue, 26 Jun 2001 16:27:13 -0400 willian (OpenSSH/i/13_session.h 1.2 644)
> > +++ 2_9_p2_w_gss_krb5_named_keys.10(w)/session.h Thu, 25 Oct 2001 14:58:32 -0400 willian (OpenSSH/i/13_session.h 1.2 644)
> > @@ -28,6 +28,7 @@
> >
> >  void	do_authenticated(Authctxt *ac);
> >
> > +int	session_still_used();
> >  int	session_open(int id);
> >  void	session_input_channel_req(int id, void *arg);
> >  void	session_close_by_pid(pid_t pid, int status);
> > Index: 2_9_p2_w_gss_krb5_named_keys.10/session.c
> > --- 2_9_p2_w_gss_krb5_named_keys.10/session.c Tue, 26 Jun 2001 16:27:13 -0400 willian (OpenSSH/i/14_session.c 1.3 644)
> > +++ 2_9_p2_w_gss_krb5_named_keys.10(w)/session.c Thu, 25 Oct 2001 15:04:21 -0400 willian (OpenSSH/i/14_session.c 1.3 644)
> > @@ -1533,18 +1533,18 @@
> >  	exit(1);
> >  }
> >
> > +static int did_init_sessions = 0;
> >  Session *
> >  session_new(void)
> >  {
> >  	int i;
> > -	static int did_init = 0;
> > -	if (!did_init) {
> > +	if (!did_init_sessions) {
> >  		debug("session_new: init");
> >  		for(i = 0; i < MAX_SESSIONS; i++) {
> >  			sessions[i].used = 0;
> >  			sessions[i].self = i;
> >  		}
> > -		did_init = 1;
> > +		did_init_sessions = 1;
> >  	}
> >  	for(i = 0; i < MAX_SESSIONS; i++) {
> >  		Session *s = &sessions[i];
> > @@ -1622,6 +1622,27 @@
> >  	error("session_by_pid: unknown pid %d", pid);
> >  	session_dump();
> >  	return NULL;
> > +}
> > +
> > +int
> > +session_still_used()
> > +{
> > +	int i;
> > +	if (!did_init_sessions) {
> > +		debug("session_new: init");
> > +		for(i = 0; i < MAX_SESSIONS; i++) {
> > +			sessions[i].used = 0;
> > +			sessions[i].self = i;
> > +		}
> > +		did_init_sessions = 1;
> > +	}
> > +	debug("session_still_used");
> > +	for(i = 0; i < MAX_SESSIONS; i++) {
> > +		Session *s = &sessions[i];
> > +		if (s->used)
> > +			return 1;
> > +	}
> > +	return 0;
> >  }
> >
> >  int
> > Index: 2_9_p2_w_gss_krb5_named_keys.10/serverloop.c
> > --- 2_9_p2_w_gss_krb5_named_keys.10/serverloop.c Thu, 03 May 2001 16:12:13 -0400 jd (OpenSSH/i/16_serverloop 1.1 644)
> > +++ 2_9_p2_w_gss_krb5_named_keys.10(w)/serverloop.c Thu, 25 Oct 2001 15:11:49 -0400 willian (OpenSSH/i/16_serverloop 1.1 644)
> > @@ -259,18 +259,25 @@
> >  		if (max_time_milliseconds == 0 || client_alive_scheduled)
> >  			max_time_milliseconds = 100;
> >
> > +	if (!channel_still_open())
> > +		max_time_milliseconds = 1000;
> > +
> >  	if (max_time_milliseconds == 0)
> >  		tvp = NULL;
> >  	else {
> >  		tv.tv_sec = max_time_milliseconds / 1000;
> >  		tv.tv_usec = 1000 * (max_time_milliseconds % 1000);
> >  		tvp = &tv;
> > +		debug3("select timeout is: tv.tv_sec=%d, tv.tv_usec=%d", (int) max_time_milliseconds /
> > +			1000, (int) 1000 * (max_time_milliseconds % 1000));
> >  	}
> >  	if (tvp!=NULL)
> >  		debug3("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds);
> >
> >  	/* Wait for something to happen, or the timeout to expire. */
> >  	ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
> > +	debug("select() returned %d, child_terminated=%d, channel_still_open() returned %d", ret,
> > +		child_terminated, channel_still_open());
> >
> >  	if (ret == -1) {
> >  		if (errno != EINTR)
> > @@ -590,6 +597,8 @@
> >  		/* Sleep in select() until we can do something. */
> >  		wait_until_can_do_something(&readset, &writeset, &max_fd,
> >  		    max_time_milliseconds);
> > +		debug("wait_until_can_do_something returned; child_terminated=%d, channel_still_open=%d",
> > +			child_terminated, channel_still_open());
> >
> >  		/* Process any channel events. */
> >  		channel_after_select(readset, writeset);
> > @@ -599,6 +608,11 @@
> >
> >  		/* Process output to the client and to program stdin. */
> >  		process_output(writeset);
> > +
> > +		/* Don't know if this is needed -- if it is, uncomment
> > +		if (!channel_still_open() && child_terminated)
> > +			break;
> > +		*/
> >  	}
> >  	if (readset)
> >  		xfree(readset);
> > @@ -721,7 +735,10 @@
> >  		if (child_terminated) {
> >  			while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
> >  				session_close_by_pid(pid, status);
> > -			child_terminated = 0;
> > +			if (session_still_used())
> > +				child_terminated = 0;
> > +		     if (child_terminated && !channel_still_open())
> > +			break;
> >  		}
> >  		if (!rekeying)
> >  			channel_after_select(readset, writeset);
> >
> --
> -DISCLAIMER: an automatically appended disclaimer may follow. By posting-
> -to a public e-mail mailing list I hereby grant permission to distribute-
> -and copy this message.-
>
> Visit our website at http://www.ubswarburg.com
>
> This message contains confidential information and is intended only
> for the individual named.  If you are not the named addressee you
> should not disseminate, distribute or copy this e-mail.  Please
> notify the sender immediately by e-mail if you have received this
> e-mail by mistake and delete this e-mail from your system.
>
> E-mail transmission cannot be guaranteed to be secure or error-free
> as information could be intercepted, corrupted, lost, destroyed,
> arrive late or incomplete, or contain viruses.  The sender therefore
> does not accept liability for any errors or omissions in the contents
> of this message which arise as a result of e-mail transmission.  If
> verification is required please request a hard-copy version.  This
> message is provided for informational purposes and should not be
> construed as a solicitation or offer to buy or sell any securities or
> related financial instruments.
>
>




More information about the openssh-unix-dev mailing list