[comp.bugs.4bsd] sendmail aborts with longjmp botch

chris@mimsy.UUCP (Chris Torek) (03/09/88)

Index: /usr/src/usr.lib/sendmail/src/usersmtp.c 4.3BSD Fix

Description:
	When processing long mailing lists, sendmail sometimes
	aborts with a `longjmp botch'.

Repeat-by:
	Difficult.

Fix:
	The problem occurs when sendmail has a read timeout of more
	than five minutes specified in the configuration file.  In
	usersmtp.c, sendmail sets up a timeout event around the
	`greeting' reply call; this timeout is scheduled to occur in
	300 seconds (5 minutes).  It then calls reply(), which calls
	sfgets(), which notes that ReadTimeout is (e.g.) 1 hour.
	sfgets sets another timeout event for one hour, reads a line,
	clears the timeout event, and returns to usersmtp, which clears
	its own read timeout.

	Suppose, however, that no reply is made within the first five
	minutes.  Then the first event, usersmtp.c's timeout, fires
	off.  This does a longjmp() all the way out of sfgets and reply
	back to usersmtp.c, which then returns to its caller, leaving
	in the event queue the event posted by sfgets.  In one hour, if
	sendmail is still running by then, this event attempts to
	longjmp back to the context set by the sfgets from reply.  This
	context is long gone and, with any luck, longjmp notices and
	aborts.

	There are several ways to fix this.  The most general would be
	for sendmail to have a `wrapper' around setjmp and longjmp that
	would provide unwind protection.  That is beyond the scope of
	this message.

	The second way is simply to remove the 5 minute timeout from
	usersmtp.c.  Commenting out the setevent() call should
	suffice.  This is somewhat undesirable but is by far the
	easiest fix.

	The third way is to have usersmtp.c arrange for ReadTimeout to
	be set to 300, preserving its old value and restoring it after
	the first call to reply.  The following UNTESTED change does
	this.

Chris

*** usersmtp.c.old	Wed Mar  9 06:08:59 1988
--- usersmtp.c	Wed Mar  9 06:10:53 1988
***************
*** 67,72 ****
  */
  
- jmp_buf	CtxGreeting;
- 
  smtpinit(m, pvp)
  	struct mailer *m;
--- 67,70 ----
***************
*** 76,80 ****
  	EVENT *gte;
  	char buf[MAXNAME];
! 	extern greettimeout();
  
  	/*
--- 74,78 ----
  	EVENT *gte;
  	char buf[MAXNAME];
! 	int oldtimeout;
  
  	/*
***************
*** 129,138 ****
  	*/
  
! 	if (setjmp(CtxGreeting) != 0)
! 		goto tempfail;
! 	gte = setevent((time_t) 300, greettimeout, 0);
  	SmtpPhase = "greeting wait";
  	r = reply(m);
! 	clrevent(gte);
  	if (r < 0 || REPLYTYPE(r) != 2)
  		goto tempfail;
--- 127,135 ----
  	*/
  
! 	oldtimeout = ReadTimeout;
! 	ReadTimeout = 5 * 60;
  	SmtpPhase = "greeting wait";
  	r = reply(m);
! 	ReadTimeout = oldtimeout;
  	if (r < 0 || REPLYTYPE(r) != 2)
  		goto tempfail;
***************
*** 213,223 ****
  }
  
- 
- static
- greettimeout()
- {
- 	/* timeout reading the greeting message */
- 	longjmp(CtxGreeting, 1);
- }
  /*
  **  SMTPRCPT -- designate recipient.
--- 210,213 ----
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

jis@BITSY.MIT.EDU (Jeffrey I. Schiller) (03/13/88)

Posting-Front-End: Gnews 1.0



	Chris's change should work. We ran into this problem awhile ago here
(with the "expert" mailing list). I thought we posted the fix (but I guess not).
In any event here are the diffs (against the version of sendmail we were using
then). This code has been in service at MIT since November of 1986.

			-Jeff

*** /tmp/,RCSt1005512	Sat Mar 12 21:22:30 1988
--- /tmp/,RCSt2005512	Sat Mar 12 21:22:37 1988
***************
*** 1,4 ****
--- 1,9 ----
  /*
+  *	$Source: /source/4.3/usr.lib/sendmail/src/RCS/usersmtp.c,v $
+  *	$Header: usersmtp.c,v 1.2 86/11/04 00:36:34 jis Exp $
+  */
+ 
+ /*
  **  Sendmail
  **  Copyright (c) 1983  Eric P. Allman
  **  Berkeley, California
***************
*** 67,74 ****
  **		creates connection and sends initial protocol.
  */
  
- jmp_buf	CtxGreeting;
- 
  smtpinit(m, pvp)
  	struct mailer *m;
  	char **pvp;
--- 72,77 ----
***************
*** 76,81 ****
--- 79,85 ----
  	register int r;
  	EVENT *gte;
  	char buf[MAXNAME];
+ 	time_t saveReadTimeout;
  	extern greettimeout();
  
  	/*
***************
*** 127,140 ****
  	**  Get the greeting message.
  	**	This should appear spontaneously.  Give it five minutes to
  	**	happen.
  	*/
  
- 	if (setjmp(CtxGreeting) != 0)
- 		goto tempfail;
- 	gte = setevent((time_t) 300, greettimeout, 0);
  	SmtpPhase = "greeting wait";
  	r = reply(m);
! 	clrevent(gte);
  	if (r < 0 || REPLYTYPE(r) != 2)
  		goto tempfail;
  
--- 131,160 ----
  	**  Get the greeting message.
  	**	This should appear spontaneously.  Give it five minutes to
  	**	happen.
+         **
+ 	**  JIS: We change the global variable ReadTimeout to be 5
+ 	**      minutes. This variable is used by the lowlevel routine
+ 	**      sfgets to determine how long to wait for input.
+ 	**      when we get our greeting we return ReadTimeout to its
+ 	**      previous state. IMPORTANT: The older code I replaced
+ 	**      used a separate timeout (via a setjmp and longjmp)
+ 	**      this LOSES REAL BIG if the 5 minute timeout goes off
+ 	**      for then sfgets gets its stack unwound and leaves
+ 	**      a lingering event that will eventually cause a longjmp
+ 	**      to some ancient stack history, sendmail then dies horribly.
+ 	**      This usually happens only when dealing with large mailing
+ 	**      lists ("xpert" in this case > 200 recipients), which is
+ 	**      the LAST place you want to dump core, for then the queue
+ 	**      files are out of date and LOTS of people get a duplicate
+ 	**      copy of the message that was in progress.
+ 	*
  	*/
  
  	SmtpPhase = "greeting wait";
+ 	saveReadTimeout = ReadTimeout;
+ 	ReadTimeout = 300;
  	r = reply(m);
! 	ReadTimeout = saveReadTimeout;
  	if (r < 0 || REPLYTYPE(r) != 2)
  		goto tempfail;
  
***************
*** 212,225 ****
    unavailable:
  	smtpquit(m);
  	return (EX_UNAVAILABLE);
- }
- 
- 
- static
- greettimeout()
- {
- 	/* timeout reading the greeting message */
- 	longjmp(CtxGreeting, 1);
  }
  /*
  **  SMTPRCPT -- designate recipient.
--- 232,237 ----

---- End of Mesage ---
--