1. Summary: syslog() uses global variable LogTag, LogFacility, and LogStat between threads. It may cause interraction between threads such as overriding global variables. 2. hardware dependency: none. 3. Description of Problem: syslog() uses global variable LogTag, LogFacility, and LogStat between threads. It may cause interraction between threads such as overriding global variables. It is really problem when using different ident, logopt, facility in multi-thread applications. Problem No.1: LogTag may be override. Problem No.2: LogFacility may be override. Problem No.3: LogStat may be override. 4. How reproducible: Always 5. Step to Reproduce: The following test program can reproduce the problems. tst-syslog.tar.gz To make reproduction easy, this program executes openlog(), syslog(), closelog() at the same time by a lot of threads. 1) Extract the archive file. The following files are extracted. Makefile tst-syslog.c 2) Compile the test program. # make 3) Run the test program. # ./tst-syslog - If Problem No.1 happens, the test program will display the following messages. Example: A00009: This is test message from thread A00006-105 In this thread, openlog("A00006",LOG_NDELAY,LOG_DEFAULT) is executed. And syslog() is excuted, expecting LogTag is "A00006", but LogTag is really "A00009", which is set by other thread. is overrided by other thread(in "A00009"). 6. Actual Results: /var/log/messages: ----------------------------------------------------------------------------- Oct 9 17:28:58 localhost A00006: This is test message from thread A00006-103 Oct 9 17:28:58 localhost A00006: This is test message from thread A00006-104 Oct 9 17:28:58 localhost A00009: This is test message from thread A00006-105 ----------------------------------------------------------------------------- In Usual, ident and Thread-ID is same, as line 1,and 2. but Problem No.1 occurs, ident and Thread-ID is different, as line 3. 7. Expected Resulst: Each thread use different values of LogType, LogFacility, and LogStat. 8. Cause Problem No.1: LogTag may be override. Thread1 Thread 2 openlog() syslog() openlog(“My ID”,...) closelog() syslog() closelog() 1) openlog() is called in thread 1 with argument "My ID" as ident. LogTag is set by "My ID". ---------------------- internal_function openlog_internal(const char *ident, int logstat, int logfac) { if (ident != NULL) LogTag = ident; ----------------------- 2) closelog()is called in thread 2. LogTag is unset. ----------------------- closelog () { /* Protect against multiple users and cancellation. */ __libc_cleanup_push (cancel_handler, NULL); __libc_lock_lock (syslog_lock); closelog_internal (); LogTag = NULL; ----------------------- 3) syslog() is called in thread1. LogTag is set by its program name, but expected value is "My ID". ------------------------- if (LogTag == NULL) LogTag = __progname; if (LogTag != NULL) fputs_unlocked (LogTag, f); (snip) /* We have the header. Print the user's format into the buffer. */ if (flag == -1) vfprintf (f, fmt, ap); else __vfprintf_chk (f, flag, fmt, ap); -------------------------- The cause of of Problem No.2, and No.3 are same. 9. Solutions I propose a following patch. ------------------------------------------------------------------------------ $ diff -ruN glibc-2.6.1/misc/syslog.c.orig glibc-2.6.1/misc/syslog.c --- glibc-2.6.1/misc/syslog.c.orig 2006-01-14 21:09:36.000000000 +0900 +++ glibc-2.6.1/misc/syslog.c 2007-10-16 17:06:53.000000000 +0900 @@ -62,6 +62,21 @@ #define ftell(s) INTUSE(_IO_ftell) (s) +#ifdef USE___THREAD +static int LogType = SOCK_DGRAM; /* type of socket connection */ +static int LogFile = -1; /* fd for log */ +static __thread int connected; /* have done connect */ +static int GLogStat; /* status bits, set by openlog() */ +static __thread int LogStat; /* status bits, set by openlog() */ +static const char *GLogTag; /* string to tag the entry with */ +static __thread const char *LogTag; /* string to tag the entry with */ +static int GLogFacility = LOG_USER; /* default facility code */ +static __thread int LogFacility = LOG_USER; /* default facility code */ +static int LogMask = 0xff; /* mask of priorities to be logged */ +static int connected_count = 0; /* open count in process */ +extern char *__progname; /* Program name, from crt0. */ + +#else static int LogType = SOCK_DGRAM; /* type of socket connection */ static int LogFile = -1; /* fd for log */ static int connected; /* have done connect */ @@ -70,6 +85,7 @@ static int LogFacility = LOG_USER; /* default facility code */ static int LogMask = 0xff; /* mask of priorities to be logged */ extern char *__progname; /* Program name, from crt0. */ +#endif /* Define the lock. */ __libc_lock_define_initialized (static, syslog_lock) @@ -201,6 +217,14 @@ __localtime_r (&now, &now_tm), _nl_C_locobj_ptr); msgoff = ftell (f); +#ifdef USE___THREAD + if ( !connected ) + { + LogTag = GLogTag; + LogStat = GLogStat; + LogFacility = GLogFacility; + } +#endif if (LogTag == NULL) LogTag = __progname; if (LogTag != NULL) @@ -335,11 +359,19 @@ internal_function openlog_internal(const char *ident, int logstat, int logfac) { +#ifdef USE___THREAD + if (ident != NULL) + GLogTag = LogTag = ident; + GLogStat = LogStat = logstat; + if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) + GLogFacility = LogFacility = logfac; +#else if (ident != NULL) LogTag = ident; LogStat = logstat; if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) LogFacility = logfac; +#endif int retry = 0; while (retry < 2) { @@ -373,8 +405,14 @@ ++retry; continue; } - } else + } + else + { connected = 1; +#ifdef USE___THREAD + connected_count++; +#endif + } } break; } @@ -406,9 +444,19 @@ if (!connected) return; +#ifndef USE___THREAD __close (LogFile); LogFile = -1; +#endif connected = 0; +#ifdef USE___THREAD + connected_count--; + if ( connected_count ) + { + __close (LogFile); + LogFile = -1; + } +#endif } void ------------------------------------------------------------------------------ This patch solves the problems that overriding gloval valiables LogTag, LogFacility, and LogStat. This fix do not effect for single-threaded applications, or multi-threaded applications which use same ident, logopt, or facilitiy between threads. Problem No.1: - This correction changes type of LogTag to thread-local storage. - Each thread may have different values of LogTag, and change local value, so able to avoid interactions to other thread. Problem No.2: - This correction changes type of LogFacility to thread-local storage. - Each thread may have different values of LogTag, and change local value, so able to avoid interactions to other thread. Problem No.3: - This correction changes type of LogStat to thread-local storage. - Each thread may have different values of LogTag, and change local value, so able to avoid interactions to other thread.
Created attachment 2047 [details] test program tst-syslog.tar.gz: test program to reproduce problems.
This is not how syslog tradtionaly was defined. Changing it now can break existing applications since changing the values in one thread won't have any effect in another thread anymore. This is a self-inflicted problem. Just don't play around with the variables.