From ga9@buffy.york.ac.uk  Wed Mar  2 15:16:13 2005
Return-Path: <ga9@buffy.york.ac.uk>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 3692A16A4CF
	for <FreeBSD-gnats-submit@freebsd.org>; Wed,  2 Mar 2005 15:16:13 +0000 (GMT)
Received: from mail-gw0.york.ac.uk (mail-gw0.york.ac.uk [144.32.128.245])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 26B8043D48
	for <FreeBSD-gnats-submit@freebsd.org>; Wed,  2 Mar 2005 15:16:11 +0000 (GMT)
	(envelope-from ga9@buffy.york.ac.uk)
Received: from buffy.york.ac.uk (buffy.york.ac.uk [144.32.226.160])
	by mail-gw0.york.ac.uk (8.12.10/8.12.10) with ESMTP id j22FG6YX019558
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 2 Mar 2005 15:16:06 GMT
Received: from buffy.york.ac.uk (localhost [127.0.0.1])
	by buffy.york.ac.uk (8.13.1/8.13.1) with ESMTP id j22FG5ja081679
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 2 Mar 2005 15:16:06 GMT
	(envelope-from ga9@buffy.york.ac.uk)
Received: (from ga9@localhost)
	by buffy.york.ac.uk (8.13.1/8.13.1/Submit) id j22FG5nL081678;
	Wed, 2 Mar 2005 15:16:05 GMT
	(envelope-from ga9)
Message-Id: <200503021516.j22FG5nL081678@buffy.york.ac.uk>
Date: Wed, 2 Mar 2005 15:16:05 GMT
From: Gavin Atkinson <gavin.atkinson@ury.york.ac.uk>
Reply-To: Gavin Atkinson <gavin.atkinson@ury.york.ac.uk>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: Signal handler abuse in comsat(8)
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         78304
>Category:       bin
>Synopsis:       Signal handler abuse in comsat(8)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Mar 02 15:20:13 GMT 2005
>Closed-Date:    Sat Jan 26 17:49:41 UTC 2008
>Last-Modified:  Sat Jan 26 17:49:41 UTC 2008
>Originator:     Gavin Atkinson
>Release:        FreeBSD 5.3-STABLE i386
>Organization:
>Environment:
System: FreeBSD buffy.york.ac.uk 5.3-STABLE FreeBSD 5.3-STABLE #0: Sat Feb 12 20:42:16 GMT 2005 root@buffy.york.ac.uk:/usr/obj/usr/src/sys/BUFFY i386

>Description:

	I've been inspired by the talk given by Henning Brauer given at
the UKUUG Winter 2005 on writing safe signal handlers, and have set about
fixing some of them in the FreeBSD source tree.

Slides for this (well, an almost identical) presentation are at
http://www.openbsd.org/papers/opencon04/index.html

	The comsat daemon hides particularly nasty abuses of signals.
In one signal handler, it failes to save and restore errno around a call
that can change it, which will cause problems if the main routine is
interrupted at the wrong time.  In another signal handler, a large number
of functions are called which are not guaranteed to be safe to call within
the context of a signal handler.  Some of these functions (e.g. realloc())
may be particularly dangerous as the handler could possibly be called
while the main routine is already in the memory allocator.  This latter
signal has been fixed to simply set a flag, which is now polled and acted
upon in the main loop.

>How-To-Repeat:
	N/A
>Fix:

	

--- comsat-sigs.patch begins here ---
Index: src/libexec/comsat/comsat.c
===================================================================
RCS file: /usr/cvs/src/libexec/comsat/comsat.c,v
retrieving revision 1.17
diff -u -r1.17 comsat.c
--- src/libexec/comsat/comsat.c	14 Feb 2005 17:42:56 -0000	1.17
+++ src/libexec/comsat/comsat.c	27 Feb 2005 23:04:47 -0000
@@ -77,11 +77,13 @@
 struct	utmp *utmp = NULL;
 time_t	lastmsgtime;
 int	nutmp, uf;
+volatile sig_atomic_t needreadutmp = 0;
 
 void jkfprintf(FILE *, char[], char[], off_t);
 void mailfor(char *);
 void notify(struct utmp *, char[], off_t, int);
 void onalrm(int);
+void readutmp(void);
 void reapchildren(int);
 
 int
@@ -109,7 +111,7 @@
 	}
 	(void)time(&lastmsgtime);
 	(void)gethostname(hostname, sizeof(hostname));
-	onalrm(0);
+	readutmp();
 	(void)signal(SIGALRM, onalrm);
 	(void)signal(SIGTTOU, SIG_IGN);
 	(void)signal(SIGCHLD, reapchildren);
@@ -121,6 +123,10 @@
 			errno = 0;
 			continue;
 		}
+		if (needreadutmp) {
+			needreadutmp = 0;
+			readutmp();
+		}
 		if (!nutmp)		/* no one has logged in yet */
 			continue;
 		sigblock(sigmask(SIGALRM));
@@ -134,12 +140,22 @@
 void
 reapchildren(int signo)
 {
-	while (wait3(NULL, WNOHANG, NULL) > 0);
+	int save_errno = errno;
+
+	while (wait3(NULL, WNOHANG, NULL) > 0)
+		;
+	errno = save_errno;
 }
 
 void
 onalrm(int signo)
 {
+	needreadutmp = 1;
+}
+
+void
+readutmp(void)
+{
 	static u_int utmpsize;		/* last malloced size for utmp */
 	static u_int utmpmtime;		/* last modification time for utmp */
 	struct stat statbf;
--- comsat-sigs.patch ends here ---


>Release-Note:
>Audit-Trail:

From: Gavin Atkinson <gavin.atkinson@ury.york.ac.uk>
To: FreeBSD-gnats-submit@FreeBSD.org, freebsd-bugs@FreeBSD.org
Cc:  
Subject: Re: bin/78304: Signal handler abuse in comsat(8)
Date: Wed, 2 Mar 2005 18:37:44 +0000 (GMT)

 Sorry, attached an earlier version of the patch by mistake.
 
 Copy of it at http://www.devrandom.co.uk/freebsd/comsat-sigs.patch incase
 my mail reader has mangled this copy.
 
 Gavin
 
 --- comsat-sigs.patch begins here ---
 Index: src/libexec/comsat/comsat.c
 ===================================================================
 RCS file: /usr/cvs/src/libexec/comsat/comsat.c,v
 retrieving revision 1.17
 diff -u -r1.17 comsat.c
 --- src/libexec/comsat/comsat.c	14 Feb 2005 17:42:56 -0000	1.17
 +++ src/libexec/comsat/comsat.c	28 Feb 2005 00:07:10 -0000
 @@ -77,11 +77,13 @@
  struct	utmp *utmp = NULL;
  time_t	lastmsgtime;
  int	nutmp, uf;
 +volatile sig_atomic_t needreadutmp = 0;
 
  void jkfprintf(FILE *, char[], char[], off_t);
  void mailfor(char *);
  void notify(struct utmp *, char[], off_t, int);
  void onalrm(int);
 +void readutmp(void);
  void reapchildren(int);
 
  int
 @@ -109,11 +111,16 @@
  	}
  	(void)time(&lastmsgtime);
  	(void)gethostname(hostname, sizeof(hostname));
 -	onalrm(0);
 +	readutmp();
  	(void)signal(SIGALRM, onalrm);
  	(void)signal(SIGTTOU, SIG_IGN);
  	(void)signal(SIGCHLD, reapchildren);
  	for (;;) {
 +		if (needreadutmp) {
 +			needreadutmp = 0;
 +			readutmp();
 +		}
 +
  		cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
  		if (cc <= 0) {
  			if (errno != EINTR)
 @@ -134,12 +141,22 @@
  void
  reapchildren(int signo)
  {
 -	while (wait3(NULL, WNOHANG, NULL) > 0);
 +	int save_errno = errno;
 +
 +	while (wait3(NULL, WNOHANG, NULL) > 0)
 +		;
 +	errno = save_errno;
  }
 
  void
  onalrm(int signo)
  {
 +	needreadutmp = 1;
 +}
 +
 +void
 +readutmp(void)
 +{
  	static u_int utmpsize;		/* last malloced size for utmp */
  	static u_int utmpmtime;		/* last modification time for utmp */
  	struct stat statbf;
 --- comsat-sigs.patch ends here ---
 

From: Bruce Evans <bde@zeta.org.au>
To: Gavin Atkinson <gavin.atkinson@ury.york.ac.uk>
Cc: FreeBSD-gnats-submit@freebsd.org
Subject: Re: bin/78304: Signal handler abuse in comsat(8)
Date: Thu, 3 Mar 2005 14:41:52 +1100 (EST)

 On Wed, 2 Mar 2005, Gavin Atkinson wrote:
 
 >> Description:
 >
 > 	I've been inspired by the talk given by Henning Brauer given at
 > the UKUUG Winter 2005 on writing safe signal handlers, and have set about
 > fixing some of them in the FreeBSD source tree.
 
 In case you fix lots of these...
 
 > while the main routine is already in the memory allocator.  This latter
 > signal has been fixed to simply set a flag, which is now polled and acted
 > upon in the main loop.
 
 ... note that simply setting a flag is usually simply wrong, since FreeBSD
 has restartable syscalls, restarting syscalls after a signal is the
 default for signal(2), and most applications and libraries don't handle
 changing the default using siginterrupt(3) or not setting the default
 using sigaction(2) (!SA_RESTART).  Restarting syscalls can result in the
 poll in the main loop never being reached, since a syscall can wait forever.
 
 Examples of utilities whose signal handling bugs have been moved by simply
 setting a flag:
 
 - ping(8).  ping's fixes are not so simple but don't quite work.  The
    resolver library sometimes hangs, and SIGINT to abort ping doesn't work
    because the resolver library restarts.  In this case, the relevant
    syscall is select() or kevent() so it isn't automatically restarted,
    and callers of these syscalls must actually do something about EINTR,
    but the resolver library doesn't know what to do and always restarts.
 
 - top(1).  top was changed to simply set a flag.  It now hangs in a
    read() from a terminal in some contexts.  It then appears to ignore
    SIGINT, but when you enter something the read completes, control
    returns to the level that polls the flag, and the SIGINT actually
    terminates the program.
 
 Fixing this in large programs is hard, but comsat seems to be simple
 enough to fix or to verify that there is no problem.  Fixing a large
 program seems to require never using automatic restarts for syscalls
 and then fixing all the bugs exposed by this, perhaps starting with
 the one in stdio (there is a PR about at least 1).
 
 Bruce
State-Changed-From-To: open->closed 
State-Changed-By: gavin 
State-Changed-When: Sat Jan 26 17:49:06 UTC 2008 
State-Changed-Why:  
I, as submitter, have no interest in this any more. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=78304 
>Unformatted:
