[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#588534: marked as done (Setting clock backwards hangs webserver)



Your message dated Sun, 01 Jan 2012 15:43:09 +0000
with message-id <E1RhNYz-0001dR-KF@franck.debian.org>
and subject line Bug#653752: Removed package(s) from unstable
has caused the Debian Bug report #588534,
regarding Setting clock backwards hangs webserver
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
588534: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=588534
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: thttpd
Version: 2.21b-11
Tags: patch


To reproduce the bug:
1) start thttpd daemon (start-up options don't matter);
2) set the system clock 10 minutes back;
3) do nothing for about 6 minutes;
4) thttpd will terminate without a log message.

The bug appears in early versions as well:
http://www.mail-archive.com/php-general@lists.php.net/msg174036.html


Reasons.

The software utilizes system time to implement a number of internal timers for:
- internal watchdog;
- periodic file mmap cache expiration check;
- periodic idle connections closing;
- periodic average rate updates for throttles;
- periodic statistics displaying;
- connections "wake-up" (recovery);
- lingering connections closing;
- CGI execution time limit implementation (CGI_TIMELIMIT).

So the problem is a scheduled time is no longer valid after system time changing. For example, current time is 15:00 and thttpd schedules some timer to expire in 5 minutes i.e. at 15:05, then system time changes (DST, manual adjusting by user / NTP cron script). As a result, one of the following happens:
1) all active timers expire too late, if system time went backwards;
2) all active timers expire too early (even immediately), if system time went forward.

In the first case, if a watchdog handler is not called within appropriate period, the program calls abort() (thttpd.c:handle_alrm()) to go out of hang state (as it's supposed to happen). Other negative consequences (normal operation interruption) are also possible.


Solution.

The patch attached checks for MONOTONIC clock availability and uses it for timers expiration evaluation instead of system time. This includes both compile-time check (for the clock access function availability) and run-time check (to ensure that monotonic clock time is fetchable).

Notes: an ideal solution should completely re-write thttpd to POSIX timers.


Best regards,
Mikhail Zolotaryov

diff -Nru thttpd-2.25b.orig/configure thttpd-2.25b/configure
--- thttpd-2.25b.orig/configure	2003-12-25 20:44:33.000000000 +0200
+++ thttpd-2.25b/configure	2010-07-08 00:07:24.000000000 +0300
@@ -2299,13 +2299,61 @@
 	;;
 esac
 
+echo $ac_n "checking for clock_gettime in -lrt""... $ac_c" 1>&6
+echo "configure:2304: checking for clock_gettime in -lrt" >&5
+ac_lib_var=`echo rt'_'clock_gettime | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lrt  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2312 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char clock_gettime();
+
+int main() {
+clock_gettime()
+; return 0; }
+EOF
+if { (eval echo configure:2323: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo rt | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lrt $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
 echo $ac_n "checking if struct tm has tm_gmtoff member""... $ac_c" 1>&6
-echo "configure:2304: checking if struct tm has tm_gmtoff member" >&5
+echo "configure:2352: checking if struct tm has tm_gmtoff member" >&5
     if eval "test \"`echo '$''{'ac_cv_acme_tm_has_tm_gmtoff'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2309 "configure"
+#line 2357 "configure"
 #include "confdefs.h"
 
 #	include <sys/types.h>
@@ -2314,7 +2362,7 @@
 u_int i = sizeof(((struct tm *)0)->tm_gmtoff)
 ; return 0; }
 EOF
-if { (eval echo configure:2318: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2366: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_acme_tm_has_tm_gmtoff=yes
 else
@@ -2334,12 +2382,12 @@
 
     fi
 echo $ac_n "checking if int64_t exists""... $ac_c" 1>&6
-echo "configure:2338: checking if int64_t exists" >&5
+echo "configure:2386: checking if int64_t exists" >&5
     if eval "test \"`echo '$''{'ac_cv_acme_int64_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2343 "configure"
+#line 2391 "configure"
 #include "confdefs.h"
 
 #	include <sys/types.h>
@@ -2347,7 +2395,7 @@
 int64_t i64
 ; return 0; }
 EOF
-if { (eval echo configure:2351: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2399: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_acme_int64_t=yes
 else
@@ -2367,12 +2415,12 @@
 
     fi
 echo $ac_n "checking if socklen_t exists""... $ac_c" 1>&6
-echo "configure:2371: checking if socklen_t exists" >&5
+echo "configure:2419: checking if socklen_t exists" >&5
     if eval "test \"`echo '$''{'ac_cv_acme_socklen_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2376 "configure"
+#line 2424 "configure"
 #include "confdefs.h"
 
 #	include <sys/types.h>
@@ -2381,7 +2429,7 @@
 socklen_t slen
 ; return 0; }
 EOF
-if { (eval echo configure:2385: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2433: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_acme_socklen_t=yes
 else
@@ -2402,7 +2450,7 @@
     fi
 
 echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
-echo "configure:2406: checking whether ${MAKE-make} sets \${MAKE}" >&5
+echo "configure:2454: checking whether ${MAKE-make} sets \${MAKE}" >&5
 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -2440,7 +2488,7 @@
 # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
 # ./install, which can be erroneously created by make from ./install.sh.
 echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
-echo "configure:2444: checking for a BSD compatible install" >&5
+echo "configure:2492: checking for a BSD compatible install" >&5
 if test -z "$INSTALL"; then
 if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
diff -Nru thttpd-2.25b.orig/configure.in thttpd-2.25b/configure.in
--- thttpd-2.25b.orig/configure.in	2003-12-25 20:41:13.000000000 +0200
+++ thttpd-2.25b/configure.in	2010-07-08 00:04:25.000000000 +0300
@@ -123,6 +123,8 @@
 	;;
 esac
 
+AC_CHECK_LIB(rt, clock_gettime)
+
 AC_ACME_TM_GMTOFF
 AC_ACME_INT64T
 AC_ACME_SOCKLENT
diff -Nru thttpd-2.25b.orig/thttpd.c thttpd-2.25b/thttpd.c
--- thttpd-2.25b.orig/thttpd.c	2003-12-25 21:06:52.000000000 +0200
+++ thttpd-2.25b/thttpd.c	2010-07-08 00:41:28.000000000 +0300
@@ -742,7 +742,7 @@
 	}
 
     /* Main loop. */
-    (void) gettimeofday( &tv, (struct timezone*) 0 );
+    tmr_prepare_timeval( &tv );
     while ( ( ! terminate ) || num_connects > 0 )
 	{
 	/* Do we need to re-open the log file? */
@@ -761,7 +761,7 @@
 	    syslog( LOG_ERR, "fdwatch - %m" );
 	    exit( 1 );
 	    }
-	(void) gettimeofday( &tv, (struct timezone*) 0 );
+	tmr_prepare_timeval( &tv );
 
 	if ( num_ready == 0 )
 	    {
diff -Nru thttpd-2.25b.orig/timers.c thttpd-2.25b/timers.c
--- thttpd-2.25b.orig/timers.c	2002-08-22 04:04:12.000000000 +0300
+++ thttpd-2.25b/timers.c	2010-07-08 11:47:03.000000000 +0300
@@ -41,7 +41,13 @@
 
 ClientData JunkClientData;
 
-
+#undef HAVE_LIBRT_MONO
+#if defined(HAVE_LIBRT) && defined(CLOCK_MONOTONIC)
+#define HAVE_LIBRT_MONO
+#include <time.h>
+static int use_monotonic = 0;		/* monotonic clock runtime availability flag */
+static struct timeval tv_diff;		/* system time - monotonic difference at start */
+#endif
 
 static unsigned int
 hash( Timer* t )
@@ -145,6 +151,26 @@
 	timers[h] = (Timer*) 0;
     free_timers = (Timer*) 0;
     alloc_count = active_count = free_count = 0;
+
+    /* Check for monotonic clock availability */
+#ifdef HAVE_LIBRT_MONO
+    struct timespec ts;
+    struct timeval tv_start, tv;
+    
+    /* Try to get monotonic clock time */
+    if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+	use_monotonic = 1;
+
+	/* Get current system time */
+	(void) gettimeofday( &tv_start , (struct timezone*) 0 );
+	tv.tv_sec = ts.tv_sec;
+	tv.tv_usec = ts.tv_nsec / 1000L;
+	/* Calculate and save the difference: tv_start is since the Epoch, so tv_start > ts
+	    tv_diff = tv_start - tv	*/
+	timersub( &tv_start, &tv, &tv_diff );
+    }
+#endif
+
     }
 
 
@@ -176,7 +202,7 @@
     if ( nowP != (struct timeval*) 0 )
 	t->time = *nowP;
     else
-	(void) gettimeofday( &t->time, (struct timezone*) 0 );
+	tmr_prepare_timeval( &t->time );
     t->time.tv_sec += msecs / 1000L;
     t->time.tv_usec += ( msecs % 1000L ) * 1000L;
     if ( t->time.tv_usec >= 1000000L )
@@ -349,3 +375,27 @@
     if ( active_count + free_count != alloc_count )
 	syslog( LOG_ERR, "timer counts don't add up!" );
     }
+
+/* Fill timeval structure for further usage by the package. */
+void
+tmr_prepare_timeval( struct timeval *tv )
+{
+#ifdef HAVE_LIBRT_MONO
+    struct timespec ts;
+    struct timeval tv0;
+
+    if (use_monotonic) {	/* use monotonic clock source ? */
+	if (clock_gettime(CLOCK_MONOTONIC,&ts) < 0) {
+	    perror("clock_gettime"); return;
+	}
+	tv0.tv_sec = ts.tv_sec;
+	tv0.tv_usec = ts.tv_nsec / 1000L;
+	/* Return system time value like it was running accurately */
+	timeradd( &tv_diff, &tv0, tv );
+    } else {
+#endif
+	(void) gettimeofday( tv , (struct timezone*) 0 );
+#ifdef HAVE_LIBRT_MONO
+    }
+#endif
+}
diff -Nru thttpd-2.25b.orig/timers.h thttpd-2.25b/timers.h
--- thttpd-2.25b.orig/timers.h	2001-04-13 08:37:41.000000000 +0300
+++ thttpd-2.25b/timers.h	2010-07-08 00:09:15.000000000 +0300
@@ -106,4 +106,7 @@
 /* Generate debugging statistics syslog message. */
 extern void tmr_logstats( long secs );
 
+/* Fill timeval structure for further usage by the package. */
+extern void tmr_prepare_timeval( struct timeval *tv );
+
 #endif /* _TIMERS_H_ */

--- End Message ---
--- Begin Message ---
Version: 2.25b-11+rm

Dear submitter,

as the package thttpd has just been removed from the Debian archive
unstable we hereby close the associated bug reports.  We are sorry
that we couldn't deal with your issue properly.

For details on the removal, please see http://bugs.debian.org/653752

The version of this package that was in Debian prior to this removal
can still be found using http://snapshot.debian.org/.

This message was generated automatically; if you believe that there is
a problem with it please contact the archive administrators by mailing
ftpmaster@debian.org.

Debian distribution maintenance software
pp.
Ansgar Burchardt (the ftpmaster behind the curtain)


--- End Message ---

Reply to: