From eugen@D00015.dialonly.kemerovo.su  Sun Jan 13 10:32:13 2002
Return-Path: <eugen@D00015.dialonly.kemerovo.su>
Received: from D00015.dialonly.kemerovo.su (www2.svzserv.kemerovo.su [213.184.65.86])
	by hub.freebsd.org (Postfix) with ESMTP id CA7B237B419
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 13 Jan 2002 10:32:07 -0800 (PST)
Received: (from eugen@localhost)
	by D00015.dialonly.kemerovo.su (8.11.6/8.11.6) id g0DIV4705875;
	Mon, 14 Jan 2002 01:31:04 +0700 (KRAT)
	(envelope-from eugen)
Message-Id: <200201131831.g0DIV4705875@D00015.dialonly.kemerovo.su>
Date: Mon, 14 Jan 2002 01:31:04 +0700 (KRAT)
From: Eugene Grosbein <eugen@grosbein.pp.ru>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: 4.5RC1: ftpd dies with SIGSEGV after ABOR
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         33846
>Category:       bin
>Synopsis:       4.5RC1: ftpd dies with SIGSEGV after ABOR
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    yar
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Jan 13 10:40:01 PST 2002
>Closed-Date:    Fri Feb 15 10:04:54 PST 2002
>Last-Modified:  Fri Feb 15 10:05:38 PST 2002
>Originator:     Eugene Grosbein
>Release:        FreeBSD 4.5-RC i386
>Organization:
Svyaz Service JSC
>Environment:
System: FreeBSD D00015.dialonly.kemerovo.su 4.5-RC FreeBSD 4.5-RC #1: Sun Jan 13 13:22:48 KRAT 2002 eu@D00015.dialonly.kemerovo.su:/usr/local/obj/mnt/old/usr/src/sys/DADV i386
Have CPUTYPE=i686 in /etc/make.conf and no other optimizations.

>Description:
	It is easy to make ftpd child to die with SIGSEGV.

>How-To-Repeat:
	
1. Have ftpd enabled in /etc/inetd.conf:

ftp    stream  tcp     nowait  root    /usr/libexec/ftpd       ftpd -llSd

   Note that flags '-llSd' are for debugging purpuses only.
   SIGSEGV is triggered with no flags too.

2. Create perl script for triggering this bug:

#!/usr/bin/perl -w

$|=1;

use strict;
use Net::FTP;

my $ftp;
my $data;
$ftp = Net::FTP->new("localhost");
$ftp->login("ftp",'');
$ftp->quot('TYPE I');
$ftp->pasv();
$ftp->retr('test');
$data=$ftp->_dataconn();
$data->reading();
$ftp->abort();

Note:
	a) you need ports/net/p5-Net to run this,
	b) hostname may not be localhost, it may be remote too,
	c) you can use anonymous or general login, it does not matter;
	   it can be chrooted or not,	
	d) name of the file does not matter too.

3. As an option:

   use sysctl kern.sugid_coredump=1;
   set sysctl kern.corefile to directory where ftpd will have write permissions
   to create core; use sysctl net.inet.tcp.log_in_vain=1, the script seldom
   fails to crash ftpd - in that case you will see 'in vain' connection
   from port 20 to randomly selected port. Just rerun script then.
   Rebuild ftpd with debug info.

Here is ftpd's example log:

Jan 14 01:23:19 D00015 ftpd[5831]: connection from localhost (127.0.0.1)
Jan 14 01:23:19 D00015 ftpd[5831]: <--- 220 
Jan 14 01:23:19 D00015 ftpd[5831]: D00015.dialonly.kemerovo.su FTP server (Version 6.00LS) ready.
Jan 14 01:23:19 D00015 ftpd[5831]: command: user ftp
Jan 14 01:23:19 D00015 ftpd[5831]: <--- 331 
Jan 14 01:23:19 D00015 ftpd[5831]: Guest login ok, send your email address as password.
Jan 14 01:23:19 D00015 ftpd[5831]: command: PASS 
Jan 14 01:23:19 D00015 ftpd[5831]: <--- 230 
Jan 14 01:23:19 D00015 ftpd[5831]: Guest login ok, access restrictions apply.
Jan 14 01:23:19 D00015 ftpd[5831]: ANONYMOUS FTP LOGIN FROM localhost, 
Jan 14 01:23:19 D00015 ftpd[5831]: command: TYPE I
Jan 14 01:23:19 D00015 ftpd[5831]: <--- 200 
Jan 14 01:23:19 D00015 ftpd[5831]: Type set to I.
Jan 14 01:23:19 D00015 ftpd[5831]: command: PASV
Jan 14 01:23:19 D00015 ftpd[5831]: <--- 227 
Jan 14 01:23:19 D00015 ftpd[5831]: Entering Passive Mode (127,0,0,1,192,115)
Jan 14 01:23:19 D00015 ftpd[5831]: command: RETR test
Jan 14 01:23:19 D00015 ftpd[5831]: <--- 150 
Jan 14 01:23:19 D00015 ftpd[5831]: Opening BINARY mode data connection for 'test' (2500 bytes).
Jan 14 01:23:19 D00015 ftpd[5831]: <--- 226 
Jan 14 01:23:19 D00015 ftpd[5831]: Transfer complete.
Jan 14 01:23:19 D00015 ftpd[5831]: get test = 2500 bytes
Jan 14 01:23:19 D00015 ftpd[5831]: command: ABOR
Jan 14 01:23:19 D00015 ftpd[5831]: <--- 426 
Jan 14 01:23:19 D00015 ftpd[5831]: Transfer aborted. Data connection closed.
Jan 14 01:23:19 D00015 ftpd[5831]: <--- 226 
Jan 14 01:23:19 D00015 ftpd[5831]: Abort successful

Here ftpd child died with SIGSEGV and we have corefile.

Here is gdb's output:

Script started on Mon Jan 14 01:11:16 2002
GNU gdb 4.18
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-unknown-freebsd"...
Core was generated by `ftpd'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /usr/lib/libskey.so.2...done.
Reading symbols from /usr/lib/libmd.so.2...done.
Reading symbols from /usr/lib/libcrypt.so.2...done.
Reading symbols from /usr/lib/libutil.so.3...done.
Reading symbols from /usr/lib/libpam.so.1...done.
Reading symbols from /usr/lib/libc.so.4...done.
Reading symbols from /usr/libexec/ld-elf.so.1...done.
#0  0x0 in ?? ()
(gdb) bt
#0  0x0 in ?? ()
#1  0x804ca4e in retrieve (cmd=0x0, 
    name=0xffff0000 <Address 0xffff0000 out of bounds>)
    at /mnt/old/usr/src/libexec/ftpd/ftpd.c:1481
#2  0x0 in ?? ()
(gdb) l 1481
1476		data = -1;
1477		pdata = -1;
1478	done:
1479		if (cmd == 0)
1480			LOGBYTES("get", name, byte_count);
1481		(*closefunc)(fin);
1482	}
1483	
1484	void
1485	store(name, mode, unique)
(gdb) q

Script done on Mon Jan 14 01:11:45 2002
 
This output is always the same for me.

>Fix:

	Unknown for me.

Eugene Grosbein
>Release-Note:
>Audit-Trail:

From: Maxim Konovalov <maxim@macomnet.ru>
To: Eugene Grosbein <eugen@grosbein.pp.ru>
Cc: FreeBSD-gnats-submit@FreeBSD.ORG, <yar@FreeBSD.ORG>,
	"Alexandr P. Kovalenko" <never@nevermind.kiev.ua>
Subject: Re: bin/33846: 4.5RC1: ftpd dies with SIGSEGV after ABOR
Date: Wed, 16 Jan 2002 12:49:10 +0300 (MSK)

 [ CC'd Yar who is working on bin/32740 and Alexandr, is a bin/32740
 originator ]
 
 Hello,
 
 This PR duplicates bin/32740:
 
 http://www.FreeBSD.org/cgi/query-pr.cgi?pr=bin/32740
 
 Here is a patch which is stolen from OpenBSD:
 
 http://www.freebsd.org/cgi/cvsweb.cgi/src/libexec/ftpd/ftpd.c?rev=1.109&content-type=text/x-cvsweb-markup&cvsroot=openbsd
 
 It works like a charm on our very busy ftp server.
 
 Btw, your How-To-Repeat section was very helpful, thanks.
 
 Index: ftpcmd.y
 ===================================================================
 RCS file: /home/ncvs/src/libexec/ftpd/ftpcmd.y,v
 retrieving revision 1.29
 diff -u -r1.29 ftpcmd.y
 --- ftpcmd.y	2002/01/05 20:13:01	1.29
 +++ ftpcmd.y	2002/01/15 13:39:10
 @@ -60,7 +60,6 @@
  #include <glob.h>
  #include <netdb.h>
  #include <pwd.h>
 -#include <setjmp.h>
  #include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
 @@ -101,6 +100,7 @@
  static	int cmd_type;
  static	int cmd_form;
  static	int cmd_bytesz;
 +static	int state;
  char	cbuf[512];
  char	*fromname = (char *) 0;
 
 @@ -750,9 +750,11 @@
  			reply(221, "Goodbye.");
  			dologout(0);
  		}
 -	| error CRLF
 +	| error
  		{
 -			yyerrok;
 +			yyclearin;		/* discard lookahead data */
 +			yyerrok;		/* clear error condition */
 +			state = 0;		/* reset lexer state */
  		}
  	;
  rcmd
 @@ -1047,8 +1049,6 @@
 
  %%
 
 -extern jmp_buf errcatch;
 -
  #define	CMD	0	/* beginning of command */
  #define	ARGS	1	/* expect miscellaneous arguments */
  #define	STR1	2	/* expect SP followed by STRING */
 @@ -1253,7 +1253,7 @@
  static int
  yylex()
  {
 -	static int cpos, state;
 +	static int cpos;
  	char *cp, *cp2;
  	struct tab *p;
  	int n;
 @@ -1290,8 +1290,7 @@
  			if (p != 0) {
  				if (p->implemented == 0) {
  					nack(p->name);
 -					longjmp(errcatch,0);
 -					/* NOTREACHED */
 +					return (LEXERR);
  				}
  				state = p->state;
  				yylval.s = p->name;
 @@ -1316,8 +1315,7 @@
  				if (p->implemented == 0) {
  					state = CMD;
  					nack(p->name);
 -					longjmp(errcatch,0);
 -					/* NOTREACHED */
 +					return (LEXERR);
  				}
  				state = p->state;
  				yylval.s = p->name;
 @@ -1467,9 +1465,8 @@
  		default:
  			fatalerror("Unknown state in scanner.");
  		}
 -		yyerror((char *) 0);
  		state = CMD;
 -		longjmp(errcatch,0);
 +		return (LEXERR);
  	}
  }
 
 Index: ftpd.c
 ===================================================================
 RCS file: /home/ncvs/src/libexec/ftpd/ftpd.c,v
 retrieving revision 1.88
 diff -u -r1.88 ftpd.c
 --- ftpd.c	2002/01/01 13:14:25	1.88
 +++ ftpd.c	2002/01/15 13:39:11
 @@ -79,7 +79,6 @@
  #include <pwd.h>
  #include <grp.h>
  #include <opie.h>
 -#include <setjmp.h>
  #include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
 @@ -125,7 +124,6 @@
 
  int	daemon_mode;
  int	data;
 -jmp_buf	errcatch, urgcatch;
  int	logged_in;
  struct	passwd *pw;
  int	ftpdebug;
 @@ -150,6 +148,7 @@
  int	noretr=0;		/* RETR command is disabled.	*/
  int	noguestretr=0;		/* RETR command is disabled for anon users. */
 
 +static volatile sig_atomic_t recvurg;
  sig_atomic_t transflag;
  off_t	file_size;
  off_t	byte_count;
 @@ -243,7 +242,8 @@
  static void	selecthost __P((union sockunion *));
  #endif
  static void	 ack __P((char *));
 -static void	 myoob __P((int));
 +static void	 sigurg __P((int));
 +static void	 myoob __P((void));
  static int	 checkuser __P((char *, char *, int));
  static FILE	*dataconn __P((char *, off_t, char *));
  static void	 dolog __P((struct sockaddr *));
 @@ -252,8 +252,9 @@
  static FILE	*getdatasock __P((char *));
  static char	*gunique __P((char *));
  static void	 lostconn __P((int));
 +static void	 sigquit __P((int));
  static int	 receive_data __P((FILE *, FILE *));
 -static void	 send_data __P((FILE *, FILE *, off_t, off_t, int));
 +static int	 send_data __P((FILE *, FILE *, off_t, off_t, int));
  static struct passwd *
  		 sgetpwnam __P((char *));
  static char	*sgetsave __P((char *));
 @@ -279,6 +280,7 @@
  	char *argv[];
  	char **envp;
  {
 +	struct sigaction sa;
  	int addrlen, ch, on = 1, tos;
  	char *cp, line[LINE_MAX];
  	FILE *fd;
 @@ -288,6 +290,8 @@
  	int	enable_v4 = 0;
 
  	tzset();		/* in case no timezone database in ~ftp */
 +	sigemptyset(&sa.sa_mask);
 +	sa.sa_flags = SA_RESTART;
 
  #ifdef OLD_SETPROCTITLE
  	/*
 @@ -419,7 +423,8 @@
  			syslog(LOG_ERR, "failed to become a daemon");
  			exit(1);
  		}
 -		(void) signal(SIGCHLD, reapchild);
 +		sa.sa_handler = reapchild;
 +		(void)sigaction(SIGCHLD, &sa, NULL);
  		/* init bind_sa */
  		memset(&hints, 0, sizeof(hints));
 
 @@ -522,11 +527,24 @@
  		}
  	}
 
 -	(void) signal(SIGCHLD, SIG_IGN);
 -	(void) signal(SIGPIPE, lostconn);
 -	if (signal(SIGURG, myoob) == SIG_ERR)
 -		syslog(LOG_ERR, "signal: %m");
 +	sa.sa_handler = SIG_DFL;
 +	(void)sigaction(SIGCHLD, &sa, NULL);
 
 +	sa.sa_handler = sigurg;
 +	sa.sa_flags = 0;		/* don't restart syscalls for SIGURG */
 +	(void)sigaction(SIGURG, &sa, NULL);
 +
 +	sigfillset(&sa.sa_mask);	/* block all signals in handler */
 +	sa.sa_flags = SA_RESTART;
 +	sa.sa_handler = sigquit;
 +	(void)sigaction(SIGHUP, &sa, NULL);
 +	(void)sigaction(SIGINT, &sa, NULL);
 +	(void)sigaction(SIGQUIT, &sa, NULL);
 +	(void)sigaction(SIGTERM, &sa, NULL);
 +
 +	sa.sa_handler = lostconn;
 +	(void)sigaction(SIGPIPE, &sa, NULL);
 +
  	addrlen = sizeof(ctrl_addr);
  	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
  		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
 @@ -610,7 +628,6 @@
  	hostname[MAXHOSTNAMELEN - 1] = '\0';
  #endif
  	reply(220, "%s FTP server (%s) ready.", hostname, version);
 -	(void) setjmp(errcatch);
  	for (;;)
  		(void) yyparse();
  	/* NOTREACHED */
 @@ -626,6 +643,15 @@
  	dologout(1);
  }
 
 +static void
 +sigquit(signo)
 +	int signo;
 +{
 +
 +	syslog(LOG_ERR, "got signal SIGQUIT");
 +        dologout(1);
 +}
 +
  #ifdef VIRTUAL_HOSTING
  /*
   * read in virtual host tables (if they exist)
 @@ -1771,7 +1797,7 @@
   *
   * NB: Form isn't handled.
   */
 -static void
 +static int
  send_data(instr, outstr, blksize, filesize, isreg)
  	FILE *instr, *outstr;
  	off_t blksize;
 @@ -1784,14 +1810,12 @@
  	off_t cnt;
 
  	transflag++;
 -	if (setjmp(urgcatch)) {
 -		transflag = 0;
 -		return;
 -	}
  	switch (type) {
 
  	case TYPE_A:
  		while ((c = getc(instr)) != EOF) {
 +			if (recvurg)
 +				goto got_oob;
  			byte_count++;
  			if (c == '\n') {
  				if (ferror(outstr))
 @@ -1807,7 +1831,7 @@
  		if (ferror(outstr))
  			goto data_err;
  		reply(226, "Transfer complete.");
 -		return;
 +		return(0);
 
  	case TYPE_I:
  	case TYPE_L:
 @@ -1829,6 +1853,8 @@
  			while (err != -1 && cnt < filesize) {
  				err = sendfile(filefd, netfd, offset, len,
  					(struct sf_hdtr *) NULL, &cnt, 0);
 +				if (recvurg)
 +					goto got_oob;
  				byte_count += cnt;
  				offset += cnt;
  				len -= cnt;
 @@ -1842,14 +1868,14 @@
  			}
 
  			reply(226, "Transfer complete.");
 -			return;
 +			return(0);
  		}
 
  oldway:
  		if ((buf = malloc((u_int)blksize)) == NULL) {
  			transflag = 0;
  			perror_reply(451, "Local resource failure: malloc");
 -			return;
 +			return(-1);
  		}
 
  		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
 @@ -1863,21 +1889,28 @@
  			goto data_err;
  		}
  		reply(226, "Transfer complete.");
 -		return;
 +		return(0);
  	default:
  		transflag = 0;
  		reply(550, "Unimplemented TYPE %d in send_data", type);
 -		return;
 +		return(-1);
  	}
 
  data_err:
  	transflag = 0;
  	perror_reply(426, "Data connection");
 -	return;
 +	return(-1);
 
  file_err:
  	transflag = 0;
  	perror_reply(551, "Error on input file");
 +	return(-1);
 +
 +got_oob:
 +	myoob();
 +	recvurg = 0;
 +	transflag = 0;
 +	return(-1);
  }
 
  /*
 @@ -1895,11 +1928,6 @@
  	char buf[BUFSIZ];
 
  	transflag++;
 -	if (setjmp(urgcatch)) {
 -		transflag = 0;
 -		return (-1);
 -	}
 -
  	bare_lfs = 0;
 
  	switch (type) {
 @@ -1907,6 +1935,8 @@
  	case TYPE_I:
  	case TYPE_L:
  		while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
 +			if (recvurg)
 +				goto got_oob;
  			if (write(fileno(outstr), buf, cnt) != cnt)
  				goto file_err;
  			byte_count += cnt;
 @@ -1923,6 +1953,8 @@
 
  	case TYPE_A:
  		while ((c = getc(instr)) != EOF) {
 +			if (recvurg)
 +				goto got_oob;
  			byte_count++;
  			if (c == '\n')
  				bare_lfs++;
 @@ -1966,6 +1998,12 @@
  	transflag = 0;
  	perror_reply(452, "Error writing file");
  	return (-1);
 +
 +got_oob:
 +	myoob();
 +	recvurg = 0;
 +	transflag = 0;
 +	return (-1);
  }
 
  void
 @@ -2385,9 +2423,16 @@
  }
 
  static void
 -myoob(signo)
 +sigurg(signo)
  	int signo;
  {
 +
 +	recvurg = 1;
 +}
 +
 +static void
 +myoob()
 +{
  	char *cp;
 
  	/* only process if transfer occurring */
 @@ -2403,7 +2448,6 @@
  		tmpline[0] = '\0';
  		reply(426, "Transfer aborted. Data connection closed.");
  		reply(226, "Abort successful");
 -		longjmp(urgcatch, 1);
  	}
  	if (strcmp(cp, "STAT\r\n") == 0) {
  		tmpline[0] = '\0';
 @@ -2719,10 +2763,6 @@
  		simple = 1;
  	}
 
 -	if (setjmp(urgcatch)) {
 -		transflag = 0;
 -		goto out;
 -	}
  	while ((dirname = *dirlist++)) {
  		if (stat(dirname, &st) < 0) {
  			/*
 @@ -2763,6 +2803,13 @@
 
  		while ((dir = readdir(dirp)) != NULL) {
  			char nbuf[MAXPATHLEN];
 +
 +			if (recvurg) {
 +				myoob();
 +				recvurg = 0;
 +				transflag = 0;
 +				goto out;
 +			}
 
  			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
  				continue;
 
 --
 Maxim Konovalov, MAcomnet, Internet-Intranet Dept., system engineer
 phone: +7 (095) 796-9079, mailto: maxim@macomnet.ru
 
 
 

From: Eugene Grosbein <eugen@www.kuzbass.ru>
To: Maxim Konovalov <maxim@macomnet.ru>
Cc: Eugene Grosbein <eugen@grosbein.pp.ru>,
	FreeBSD-gnats-submit@FreeBSD.ORG, yar@FreeBSD.ORG,
	"Alexandr P. Kovalenko" <never@nevermind.kiev.ua>
Subject: Re: bin/33846: 4.5RC1: ftpd dies with SIGSEGV after ABOR
Date: Wed, 16 Jan 2002 17:27:38 +0700

 Maxim Konovalov wrote:
 
 > Here is a patch which is stolen from OpenBSD:
 
 It's for CURRENT and I can't test it as I do not run CURRENT yet.

From: Maxim Konovalov <maxim@macomnet.ru>
To: Eugene Grosbein <eugen@www.kuzbass.ru>
Cc: Eugene Grosbein <eugen@grosbein.pp.ru>,
	<FreeBSD-gnats-submit@FreeBSD.ORG>, <yar@FreeBSD.ORG>,
	"Alexandr P. Kovalenko" <never@nevermind.kiev.ua>
Subject: Re: bin/33846: 4.5RC1: ftpd dies with SIGSEGV after ABOR
Date: Wed, 16 Jan 2002 13:40:30 +0300 (MSK)

 On 17:27+0700, Jan 16, 2002, Eugene Grosbein wrote:
 
 > Maxim Konovalov wrote:
 >
 > > Here is a patch which is stolen from OpenBSD:
 >
 > It's for CURRENT and I can't test it as I do not run CURRENT yet.
 
 try http://news1.macomnet.ru/~maxim/p/ftpd.patch.stable
 
 -- 
 Maxim Konovalov, MAcomnet, Internet-Intranet Dept., system engineer
 phone: +7 (095) 796-9079, mailto: maxim@macomnet.ru
 
Responsible-Changed-From-To: freebsd-bugs->yar 
Responsible-Changed-By: sheldonh 
Responsible-Changed-When: Wed Jan 16 06:11:25 PST 2002 
Responsible-Changed-Why:  
According to the audit trail, this ties in with work Yar's already doing 
on PR bin/32740. 

http://www.FreeBSD.org/cgi/query-pr.cgi?pr=33846 

From: Yar Tikhiy <yar@FreeBSD.ORG>
To: Maxim Konovalov <maxim@macomnet.ru>
Cc: Eugene Grosbein <eugen@grosbein.pp.ru>,
	FreeBSD-gnats-submit@FreeBSD.ORG,
	"Alexandr P. Kovalenko" <never@nevermind.kiev.ua>
Subject: Re: bin/33846: 4.5RC1: ftpd dies with SIGSEGV after ABOR
Date: Thu, 17 Jan 2002 12:32:50 +0300

 On Wed, Jan 16, 2002 at 12:49:10PM +0300, Maxim Konovalov wrote:
 > 
 > This PR duplicates bin/32740:
 > 
 > http://www.FreeBSD.org/cgi/query-pr.cgi?pr=bin/32740
 > 
 > Here is a patch which is stolen from OpenBSD:
  
 > http://www.freebsd.org/cgi/cvsweb.cgi/src/libexec/ftpd/ftpd.c?rev=1.109&content-type=text/x-cvsweb-markup&cvsroot=openbsd
 > 
 > It works like a charm on our very busy ftp server.
 
 Maxim, thank you for your patch.  It really works.
 
 The only issue that came to my sight is that in this patch, the
 "recvurg" flag is checked only inside i/o loops, i.e., when an
 i/o operation returned success.  Shouldn't the flag be checked
 after such loops as well?  What if an i/o operation fails with
 EINTR on SIGURG?
 
 -- 
 Yar

From: Maxim Konovalov <maxim@macomnet.ru>
To: Yar Tikhiy <yar@FreeBSD.ORG>
Cc: Eugene Grosbein <eugen@grosbein.pp.ru>,
	<FreeBSD-gnats-submit@FreeBSD.ORG>,
	"Alexandr P. Kovalenko" <never@nevermind.kiev.ua>
Subject: Re: bin/33846: 4.5RC1: ftpd dies with SIGSEGV after ABOR
Date: Thu, 17 Jan 2002 17:10:25 +0300 (MSK)

 Yar,
 
 On 12:32+0300, Jan 17, 2002, Yar Tikhiy wrote:
 
 > On Wed, Jan 16, 2002 at 12:49:10PM +0300, Maxim Konovalov wrote:
 > >
 > > This PR duplicates bin/32740:
 > >
 > > http://www.FreeBSD.org/cgi/query-pr.cgi?pr=bin/32740
 > >
 > > Here is a patch which is stolen from OpenBSD:
 >
 > > http://www.freebsd.org/cgi/cvsweb.cgi/src/libexec/ftpd/ftpd.c?rev=1.109&content-type=text/x-cvsweb-markup&cvsroot=openbsd
 > >
 > > It works like a charm on our very busy ftp server.
 >
 > Maxim, thank you for your patch.  It really works.
 
 In a private conversation Eugene said that he has some problems with
 the patch. I am waiting for the details from him.
 
 > The only issue that came to my sight is that in this patch, the
 > "recvurg" flag is checked only inside i/o loops, i.e., when an
 > i/o operation returned success.  Shouldn't the flag be checked
 > after such loops as well?  What if an i/o operation fails with
 > EINTR on SIGURG?
 
 Well, you are quite right. We have to check the recvurg flag after any
 i/o cycles which can be interrupted by SIGURG too.
 
 -- 
 Maxim Konovalov, MAcomnet, Internet-Intranet Dept., system engineer
 phone: +7 (095) 796-9079, mailto: maxim@macomnet.ru
 
 

From: Maxim Konovalov <maxim@macomnet.ru>
To: Yar Tikhiy <yar@FreeBSD.ORG>
Cc: Eugene Grosbein <eugen@grosbein.pp.ru>,
	<FreeBSD-gnats-submit@FreeBSD.ORG>,
	"Alexandr P. Kovalenko" <never@nevermind.kiev.ua>
Subject: Re: bin/33846: 4.5RC1: ftpd dies with SIGSEGV after ABOR
Date: Mon, 21 Jan 2002 15:39:08 +0300 (MSK)

 Hello,
 
 [...]
 > > Maxim, thank you for your patch.  It really works.
 >
 > In a private conversation Eugene said that he has some problems with
 > the patch. I am waiting for the details from him.
 
 Solved.
 
 > > The only issue that came to my sight is that in this patch, the
 > > "recvurg" flag is checked only inside i/o loops, i.e., when an
 > > i/o operation returned success.  Shouldn't the flag be checked
 > > after such loops as well?  What if an i/o operation fails with
 > > EINTR on SIGURG?
 >
 > Well, you are quite right. We have to check the recvurg flag after any
 > i/o cycles which can be interrupted by SIGURG too.
 
 Here is a corrected patch. I've added an additional check after each
 i/o cycle which can be interrupted by SIGURG. sendfile(2) and
 readdir(3) are not ones.
 
 Again, the patch is against -current, tested on our -stable ftp
 server.
 
 Index: ftpcmd.y
 ===================================================================
 RCS file: /home/ncvs/src/libexec/ftpd/ftpcmd.y,v
 retrieving revision 1.29
 diff -u -r1.29 ftpcmd.y
 --- ftpcmd.y	2002/01/05 20:13:01	1.29
 +++ ftpcmd.y	2002/01/21 09:50:52
 @@ -60,7 +60,6 @@
  #include <glob.h>
  #include <netdb.h>
  #include <pwd.h>
 -#include <setjmp.h>
  #include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
 @@ -101,6 +100,7 @@
  static	int cmd_type;
  static	int cmd_form;
  static	int cmd_bytesz;
 +static	int state;
  char	cbuf[512];
  char	*fromname = (char *) 0;
 
 @@ -750,9 +750,11 @@
  			reply(221, "Goodbye.");
  			dologout(0);
  		}
 -	| error CRLF
 +	| error
  		{
 -			yyerrok;
 +			yyclearin;		/* discard lookahead data */
 +			yyerrok;		/* clear error condition */
 +			state = 0;		/* reset lexer state */
  		}
  	;
  rcmd
 @@ -1047,8 +1049,6 @@
 
  %%
 
 -extern jmp_buf errcatch;
 -
  #define	CMD	0	/* beginning of command */
  #define	ARGS	1	/* expect miscellaneous arguments */
  #define	STR1	2	/* expect SP followed by STRING */
 @@ -1253,7 +1253,7 @@
  static int
  yylex()
  {
 -	static int cpos, state;
 +	static int cpos;
  	char *cp, *cp2;
  	struct tab *p;
  	int n;
 @@ -1290,8 +1290,7 @@
  			if (p != 0) {
  				if (p->implemented == 0) {
  					nack(p->name);
 -					longjmp(errcatch,0);
 -					/* NOTREACHED */
 +					return (LEXERR);
  				}
  				state = p->state;
  				yylval.s = p->name;
 @@ -1316,8 +1315,7 @@
  				if (p->implemented == 0) {
  					state = CMD;
  					nack(p->name);
 -					longjmp(errcatch,0);
 -					/* NOTREACHED */
 +					return (LEXERR);
  				}
  				state = p->state;
  				yylval.s = p->name;
 @@ -1467,9 +1465,8 @@
  		default:
  			fatalerror("Unknown state in scanner.");
  		}
 -		yyerror((char *) 0);
  		state = CMD;
 -		longjmp(errcatch,0);
 +		return (LEXERR);
  	}
  }
 
 Index: ftpd.c
 ===================================================================
 RCS file: /home/ncvs/src/libexec/ftpd/ftpd.c,v
 retrieving revision 1.93
 diff -u -r1.93 ftpd.c
 --- ftpd.c	2002/01/19 18:29:50	1.93
 +++ ftpd.c	2002/01/21 09:50:53
 @@ -79,7 +79,6 @@
  #include <pwd.h>
  #include <grp.h>
  #include <opie.h>
 -#include <setjmp.h>
  #include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
 @@ -125,7 +124,6 @@
 
  int	daemon_mode;
  int	data;
 -jmp_buf	errcatch, urgcatch;
  int	logged_in;
  struct	passwd *pw;
  int	ftpdebug;
 @@ -150,6 +148,7 @@
  int	noretr=0;		/* RETR command is disabled.	*/
  int	noguestretr=0;		/* RETR command is disabled for anon users. */
 
 +static volatile sig_atomic_t recvurg;
  sig_atomic_t transflag;
  off_t	file_size;
  off_t	byte_count;
 @@ -243,7 +242,8 @@
  static void	selecthost __P((union sockunion *));
  #endif
  static void	 ack __P((char *));
 -static void	 myoob __P((int));
 +static void	 sigurg __P((int));
 +static void	 myoob __P((void));
  static int	 checkuser __P((char *, char *, int));
  static FILE	*dataconn __P((char *, off_t, char *));
  static void	 dolog __P((struct sockaddr *));
 @@ -252,8 +252,9 @@
  static FILE	*getdatasock __P((char *));
  static char	*gunique __P((char *));
  static void	 lostconn __P((int));
 +static void	 sigquit __P((int));
  static int	 receive_data __P((FILE *, FILE *));
 -static void	 send_data __P((FILE *, FILE *, off_t, off_t, int));
 +static int	 send_data __P((FILE *, FILE *, off_t, off_t, int));
  static struct passwd *
  		 sgetpwnam __P((char *));
  static char	*sgetsave __P((char *));
 @@ -279,6 +280,7 @@
  	char *argv[];
  	char **envp;
  {
 +	struct sigaction sa;
  	int addrlen, ch, on = 1, tos;
  	char *cp, line[LINE_MAX];
  	FILE *fd;
 @@ -288,6 +290,8 @@
  	int	enable_v4 = 0;
 
  	tzset();		/* in case no timezone database in ~ftp */
 +	sigemptyset(&sa.sa_mask);
 +	sa.sa_flags = SA_RESTART;
 
  #ifdef OLD_SETPROCTITLE
  	/*
 @@ -419,7 +423,8 @@
  			syslog(LOG_ERR, "failed to become a daemon");
  			exit(1);
  		}
 -		(void) signal(SIGCHLD, reapchild);
 +		sa.sa_handler = reapchild;
 +		(void)sigaction(SIGCHLD, &sa, NULL);
  		/* init bind_sa */
  		memset(&hints, 0, sizeof(hints));
 
 @@ -522,11 +527,24 @@
  		}
  	}
 
 -	(void) signal(SIGCHLD, SIG_IGN);
 -	(void) signal(SIGPIPE, lostconn);
 -	if (signal(SIGURG, myoob) == SIG_ERR)
 -		syslog(LOG_ERR, "signal: %m");
 +	sa.sa_handler = SIG_DFL;
 +	(void)sigaction(SIGCHLD, &sa, NULL);
 
 +	sa.sa_handler = sigurg;
 +	sa.sa_flags = 0;		/* don't restart syscalls for SIGURG */
 +	(void)sigaction(SIGURG, &sa, NULL);
 +
 +	sigfillset(&sa.sa_mask);	/* block all signals in handler */
 +	sa.sa_flags = SA_RESTART;
 +	sa.sa_handler = sigquit;
 +	(void)sigaction(SIGHUP, &sa, NULL);
 +	(void)sigaction(SIGINT, &sa, NULL);
 +	(void)sigaction(SIGQUIT, &sa, NULL);
 +	(void)sigaction(SIGTERM, &sa, NULL);
 +
 +	sa.sa_handler = lostconn;
 +	(void)sigaction(SIGPIPE, &sa, NULL);
 +
  	addrlen = sizeof(ctrl_addr);
  	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
  		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
 @@ -610,7 +628,6 @@
  	hostname[MAXHOSTNAMELEN - 1] = '\0';
  #endif
  	reply(220, "%s FTP server (%s) ready.", hostname, version);
 -	(void) setjmp(errcatch);
  	for (;;)
  		(void) yyparse();
  	/* NOTREACHED */
 @@ -626,6 +643,15 @@
  	dologout(1);
  }
 
 +static void
 +sigquit(signo)
 +	int signo;
 +{
 +
 +	syslog(LOG_ERR, "got signal SIGQUIT");
 +        dologout(1);
 +}
 +
  #ifdef VIRTUAL_HOSTING
  /*
   * read in virtual host tables (if they exist)
 @@ -1771,7 +1797,7 @@
   *
   * NB: Form isn't handled.
   */
 -static void
 +static int
  send_data(instr, outstr, blksize, filesize, isreg)
  	FILE *instr, *outstr;
  	off_t blksize;
 @@ -1784,14 +1810,12 @@
  	off_t cnt;
 
  	transflag++;
 -	if (setjmp(urgcatch)) {
 -		transflag = 0;
 -		return;
 -	}
  	switch (type) {
 
  	case TYPE_A:
  		while ((c = getc(instr)) != EOF) {
 +			if (recvurg)
 +				goto got_oob;
  			byte_count++;
  			if (c == '\n') {
  				if (ferror(outstr))
 @@ -1800,6 +1824,8 @@
  			}
  			(void) putc(c, outstr);
  		}
 +		if (recvurg)
 +			goto got_oob;
  		fflush(outstr);
  		transflag = 0;
  		if (ferror(instr))
 @@ -1807,7 +1833,7 @@
  		if (ferror(outstr))
  			goto data_err;
  		reply(226, "Transfer complete.");
 -		return;
 +		return(0);
 
  	case TYPE_I:
  	case TYPE_L:
 @@ -1829,6 +1855,8 @@
  			while (err != -1 && cnt < filesize) {
  				err = sendfile(filefd, netfd, offset, len,
  					(struct sf_hdtr *) NULL, &cnt, 0);
 +				if (recvurg)
 +					goto got_oob;
  				byte_count += cnt;
  				offset += cnt;
  				len -= cnt;
 @@ -1842,14 +1870,14 @@
  			}
 
  			reply(226, "Transfer complete.");
 -			return;
 +			return(0);
  		}
 
  oldway:
  		if ((buf = malloc((u_int)blksize)) == NULL) {
  			transflag = 0;
  			perror_reply(451, "Local resource failure: malloc");
 -			return;
 +			return(-1);
  		}
 
  		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
 @@ -1863,21 +1891,28 @@
  			goto data_err;
  		}
  		reply(226, "Transfer complete.");
 -		return;
 +		return(0);
  	default:
  		transflag = 0;
  		reply(550, "Unimplemented TYPE %d in send_data", type);
 -		return;
 +		return(-1);
  	}
 
  data_err:
  	transflag = 0;
  	perror_reply(426, "Data connection");
 -	return;
 +	return(-1);
 
  file_err:
  	transflag = 0;
  	perror_reply(551, "Error on input file");
 +	return(-1);
 +
 +got_oob:
 +	myoob();
 +	recvurg = 0;
 +	transflag = 0;
 +	return(-1);
  }
 
  /*
 @@ -1895,11 +1930,6 @@
  	char buf[BUFSIZ];
 
  	transflag++;
 -	if (setjmp(urgcatch)) {
 -		transflag = 0;
 -		return (-1);
 -	}
 -
  	bare_lfs = 0;
 
  	switch (type) {
 @@ -1907,10 +1937,14 @@
  	case TYPE_I:
  	case TYPE_L:
  		while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
 +			if (recvurg)
 +				goto got_oob;
  			if (write(fileno(outstr), buf, cnt) != cnt)
  				goto file_err;
  			byte_count += cnt;
  		}
 +		if (recvurg)
 +			goto got_oob;
  		if (cnt < 0)
  			goto data_err;
  		transflag = 0;
 @@ -1923,6 +1957,8 @@
 
  	case TYPE_A:
  		while ((c = getc(instr)) != EOF) {
 +			if (recvurg)
 +				goto got_oob;
  			byte_count++;
  			if (c == '\n')
  				bare_lfs++;
 @@ -1938,6 +1974,8 @@
  			(void) putc(c, outstr);
  	contin2:	;
  		}
 +		if (recvurg)
 +			goto got_oob;
  		fflush(outstr);
  		if (ferror(instr))
  			goto data_err;
 @@ -1966,6 +2004,12 @@
  	transflag = 0;
  	perror_reply(452, "Error writing file");
  	return (-1);
 +
 +got_oob:
 +	myoob();
 +	recvurg = 0;
 +	transflag = 0;
 +	return (-1);
  }
 
  void
 @@ -2385,9 +2429,16 @@
  }
 
  static void
 -myoob(signo)
 +sigurg(signo)
  	int signo;
  {
 +
 +	recvurg = 1;
 +}
 +
 +static void
 +myoob()
 +{
  	char *cp;
 
  	/* only process if transfer occurring */
 @@ -2403,7 +2454,6 @@
  		tmpline[0] = '\0';
  		reply(426, "Transfer aborted. Data connection closed.");
  		reply(226, "Abort successful");
 -		longjmp(urgcatch, 1);
  	}
  	if (strcmp(cp, "STAT\r\n") == 0) {
  		tmpline[0] = '\0';
 @@ -2719,10 +2769,6 @@
  		simple = 1;
  	}
 
 -	if (setjmp(urgcatch)) {
 -		transflag = 0;
 -		goto out;
 -	}
  	while ((dirname = *dirlist++)) {
  		if (stat(dirname, &st) < 0) {
  			/*
 @@ -2763,6 +2809,13 @@
 
  		while ((dir = readdir(dirp)) != NULL) {
  			char nbuf[MAXPATHLEN];
 +
 +			if (recvurg) {
 +				myoob();
 +				recvurg = 0;
 +				transflag = 0;
 +				goto out;
 +			}
 
  			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
  				continue;
 
 --
 Maxim Konovalov, MAcomnet, Internet-Intranet Dept., system engineer
 phone: +7 (095) 796-9079, mailto: maxim@macomnet.ru
 
State-Changed-From-To: open->analyzed 
State-Changed-By: yar 
State-Changed-When: Mon Jan 28 11:32:19 PST 2002 
State-Changed-Why:  
The patch applied to -current with minor style(9) fixes 
and committed.  Let the community do further testing. 
Thanks a lot! 

http://www.FreeBSD.org/cgi/query-pr.cgi?pr=33846 
State-Changed-From-To: analyzed->closed 
State-Changed-By: yar 
State-Changed-When: Fri Feb 15 10:04:54 PST 2002 
State-Changed-Why:  
The tested fix committed to both active branches. Thanks! 

http://www.FreeBSD.org/cgi/query-pr.cgi?pr=33846 
>Unformatted:
