From dan@dan.emsphone.com  Wed Oct  6 17:23:41 2004
Return-Path: <dan@dan.emsphone.com>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id D829316A4CE
	for <FreeBSD-gnats-submit@freebsd.org>; Wed,  6 Oct 2004 17:23:41 +0000 (GMT)
Received: from dan.emsphone.com (dan.emsphone.com [199.67.51.101])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 6497243D45
	for <FreeBSD-gnats-submit@freebsd.org>; Wed,  6 Oct 2004 17:23:41 +0000 (GMT)
	(envelope-from dan@dan.emsphone.com)
Received: (from dan@localhost)
	by dan.emsphone.com (8.12.11/8.12.11) id i96HNeT0099891;
	Wed, 6 Oct 2004 12:23:40 -0500 (CDT)
	(envelope-from dan)
Message-Id: <200410061723.i96HNeT0099891@dan.emsphone.com>
Date: Wed, 6 Oct 2004 12:23:40 -0500 (CDT)
From: Dan Nelson <dnelson@allantgroup.com>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: [PATCH] syslog is not thread-safe
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         72394
>Category:       bin
>Synopsis:       [PATCH] syslog is not thread-safe
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    glebius
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Oct 06 17:30:26 GMT 2004
>Closed-Date:    Thu Jan 13 09:24:10 GMT 2005
>Last-Modified:  Thu Jan 13 09:24:10 GMT 2005
>Originator:     Dan Nelson
>Release:        FreeBSD 5.3-BETA7 i386
>Organization:
The Allant Group, Inc.
>Environment:
System: FreeBSD dan.emsphone.com 5.3-BETA7 FreeBSD 5.3-BETA7 #361: Tue Oct 5 16:17:41 CDT 2004 zsh@dan.emsphone.com:/usr/src/sys/i386/compile/DANSMP i386


	
>Description:
	

The syslog functions are not in the list of thread-unsafe functions at
http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html#tag_02_09_01
, so they are supposed to be safe.  The log fd is not locked, though,
so simultanteous calls to openlog/syslog/closelog may result in lost
entries or lost fds.

>How-To-Repeat:
	

Compile this with kse or thr threads, run it, and count how many log
entries end up in /var/log/messages.  You may end up with missing
lines, or kernel messages saying "unp_connect(): lost race to another
thread".  The closelog() forces an openlog() on the next call to
syslog().  A more likely failure case would be if someone bounced
syslogd, which will force every process with an open log fd to reopen
it.  If openlog/closelog is never called from within a thread and the
fd stays open, syslog() itself is already thread-safe.

#include <pthread.h>
#include <syslog.h>
#include <stdarg.h>

void *logit(void *arg)
{
	char *threadnum = arg;
	int i;
	for (i = 0; i < 100; i++)
	{
		syslog(LOG_WARNING, "I'm thread %s, log entry %03d", threadnum, i+1);
		closelog();
	}
	return 0;
}

int main(void)
{
	pthread_t t1, t2;
	pthread_create(&t1, NULL, &logit, "1");
	pthread_create(&t2, NULL, &logit, "2");
	pthread_join(t1, NULL);
	pthread_join(t2, NULL);
	syslog(LOG_WARNING, "Done");
	return 0;
}


>Fix:

	

Index: lib/libc/gen/syslog.c
===================================================================
RCS file: /home/ncvs/src/lib/libc/gen/syslog.c,v
retrieving revision 1.30
diff -u -p -r1.30 syslog.c
--- lib/libc/gen/syslog.c	10 May 2004 17:12:52 -0000	1.30
+++ lib/libc/gen/syslog.c	6 Oct 2004 17:05:42 -0000
@@ -48,6 +48,7 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/sys
 #include <errno.h>
 #include <fcntl.h>
 #include <paths.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -62,10 +63,20 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/sys
 static int	LogFile = -1;		/* fd for log */
 static int	connected;		/* have done connect */
 static int	opened;			/* have done openlog() */
-static int	LogStat = 0;		/* status bits, set by openlog() */
+static int	LogStat;		/* status bits, set by openlog() */
 static const char *LogTag = NULL;	/* string to tag the entry with */
 static int	LogFacility = LOG_USER;	/* default facility code */
 static int	LogMask = 0xff;		/* mask of priorities to be logged */
+static pthread_mutex_t	syslog_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+#define	THREAD_LOCK() \
+	do { \
+		if (__isthreaded) _pthread_mutex_lock(&syslog_mutex); \
+	} while(0)
+#define	THREAD_UNLOCK()	\
+	do { \
+		if (__isthreaded) _pthread_mutex_unlock(&syslog_mutex); \
+	} while(0)
 
 static void	disconnectlog(void); /* disconnect from syslogd */
 static void	connectlog(void);	/* (re)connect to syslogd */
@@ -141,11 +152,15 @@ vsyslog(pri, fmt, ap)
 		pri &= LOG_PRIMASK|LOG_FACMASK;
 	}
 
+	saved_errno = errno;
+
+	THREAD_LOCK();
+
 	/* Check priority against setlogmask values. */
-	if (!(LOG_MASK(LOG_PRI(pri)) & LogMask))
+	if (!(LOG_MASK(LOG_PRI(pri)) & LogMask)) {
+		THREAD_UNLOCK();
 		return;
-
-	saved_errno = errno;
+	}
 
 	/* Set default facility if none specified. */
 	if ((pri & LOG_FACMASK) == 0)
@@ -155,8 +170,10 @@ vsyslog(pri, fmt, ap)
 	tbuf_cookie.base = tbuf;
 	tbuf_cookie.left = sizeof(tbuf);
 	fp = fwopen(&tbuf_cookie, writehook);
-	if (fp == NULL)
+	if (fp == NULL) {
+		THREAD_UNLOCK();
 		return;
+	}
 
 	/* Build the message. */
 	(void)time(&now);
@@ -186,6 +203,7 @@ vsyslog(pri, fmt, ap)
 		fmt_fp = fwopen(&fmt_cookie, writehook);
 		if (fmt_fp == NULL) {
 			fclose(fp);
+			THREAD_UNLOCK();
 			return;
 		}
 
@@ -243,8 +261,10 @@ vsyslog(pri, fmt, ap)
 	if (!opened)
 		openlog(LogTag, LogStat | LOG_NDELAY, 0);
 	connectlog();
-	if (send(LogFile, tbuf, cnt, 0) >= 0)
+	if (send(LogFile, tbuf, cnt, 0) >= 0) {
+		THREAD_UNLOCK();
 		return;
+	}
 
 	/*
 	 * If the send() failed, the odds are syslogd was restarted.
@@ -252,8 +272,10 @@ vsyslog(pri, fmt, ap)
 	 */
 	disconnectlog();
 	connectlog();
-	if (send(LogFile, tbuf, cnt, 0) >= 0)
+	if (send(LogFile, tbuf, cnt, 0) >= 0) {
+		THREAD_UNLOCK();
 		return;
+	}
 
 	/*
 	 * Output the message to the console; try not to block
@@ -274,6 +296,7 @@ vsyslog(pri, fmt, ap)
 		(void)_writev(fd, iov, 2);
 		(void)_close(fd);
 	}
+	THREAD_UNLOCK();
 }
 static void
 disconnectlog()
@@ -332,6 +355,7 @@ openlog(ident, logstat, logfac)
 	const char *ident;
 	int logstat, logfac;
 {
+	THREAD_LOCK();
 	if (ident != NULL)
 		LogTag = ident;
 	LogStat = logstat;
@@ -342,15 +366,18 @@ openlog(ident, logstat, logfac)
 		connectlog();
 
 	opened = 1;	/* ident and facility has been set */
+	THREAD_UNLOCK();
 }
 
 void
 closelog()
 {
+	THREAD_LOCK();
 	(void)_close(LogFile);
 	LogFile = -1;
 	LogTag = NULL;
 	connected = 0;
+	THREAD_UNLOCK();
 }
 
 /* setlogmask -- set the log mask level */
@@ -360,8 +387,10 @@ setlogmask(pmask)
 {
 	int omask;
 
+	THREAD_LOCK();
 	omask = LogMask;
 	if (pmask != 0)
 		LogMask = pmask;
+	THREAD_UNLOCK();
 	return (omask);
 }

>Release-Note:
>Audit-Trail:

From: Dan Nelson <dnelson@allantgroup.com>
To: FreeBSD-gnats-submit@FreeBSD.org
Cc:  
Subject: Re: standards/72394: [PATCH] syslog is not thread-safe
Date: Thu, 7 Oct 2004 00:58:43 -0500

 Update: if errno isn't valid, strerror() isn't thread-safe, so here's
 an addon patch that uses strerror_r instead:
 
 --- syslog.c~	Thu Oct  7 00:39:44 2004
 +++ syslog.c	Thu Oct  7 00:49:06 2004
 @@ -139,7 +139,7 @@ vsyslog(pri, fmt, ap)
  	char ch, *p;
  	time_t now;
  	int fd, saved_errno;
 -	char *stdp, tbuf[2048], fmt_cpy[1024], timbuf[26];
 +	char *stdp, tbuf[2048], fmt_cpy[1024], timbuf[26], errstr[64];
  	FILE *fp, *fmt_fp;
  	struct bufcookie tbuf_cookie;
  	struct bufcookie fmt_cookie;
 @@ -215,7 +215,8 @@ vsyslog(pri, fmt, ap)
  		for ( ; (ch = *fmt); ++fmt) {
  			if (ch == '%' && fmt[1] == 'm') {
  				++fmt;
 -				fputs(strerror(saved_errno), fmt_fp);
 +				strerror_r(saved_errno, errstr, sizeof(errstr));
 +				fputs(errstr, fmt_fp);
  			} else if (ch == '%' && fmt[1] == '%') {
  				++fmt;
  				fputc(ch, fmt_fp);
 
 
Responsible-Changed-From-To: freebsd-standards->freebsd-bugs 
Responsible-Changed-By: linimon 
Responsible-Changed-When: Sat Oct 30 07:15:43 GMT 2004 
Responsible-Changed-Why:  
This sounds more like a kern bug than something for the standards folks. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=72394 
Responsible-Changed-From-To: freebsd-bugs->glebius 
Responsible-Changed-By: glebius 
Responsible-Changed-When: Thu Nov 11 11:25:04 GMT 2004 
Responsible-Changed-Why:  
I'll handle it. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=72394 
State-Changed-From-To: open->patched 
State-Changed-By: glebius 
State-Changed-When: Thu Dec 30 16:03:53 GMT 2004 
State-Changed-Why:  
Commited, thanks! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=72394 
State-Changed-From-To: patched->closed 
State-Changed-By: glebius 
State-Changed-When: Thu Jan 13 09:23:53 GMT 2005 
State-Changed-Why:  
MFC done. 

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