Subject: lib/23613: sleep() misbehaves for values > 1000000000
To: None <gnats-bugs@gnats.netbsd.org>
From: Christian Biere <christianbiere@gmx.de>
List: netbsd-bugs
Date: 12/01/2003 21:37:54
>Number:         23613
>Category:       lib
>Synopsis:       sleep() misbehaves for values > 1000000000
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Dec 01 21:38:00 UTC 2003
>Closed-Date:
>Last-Modified:
>Originator:     
>Release:        NetBSD 1.6ZF
>Organization:
>Environment:
System: NetBSD cyclonus 1.6ZF NetBSD 1.6ZF (STARSCREAM) #0: Sun Nov 30 01:56:21 CET 2003 bin@cyclonus:/usr/build/obj/sys/arch/i386/compile/STARSCREAM i386
Architecture: i386
Machine: i386
>Description:

sleep() returns bogus results if the parameter is greater than 1000000000
and returns immediately. The later is caused by nanosleep() which
doesn't allow to sleep for more than 1Gs. The bogus result is probably
caused by the naive sleep() which doesn't check for (errno == EINVAL)
(maybe because it's undocumented) in which case nanosleep (probably)
doesn't set ``rmtp''.

I wonder where the 1000000000 limit comes from and I'd suggest to use
a macro definition instead.  It's undocumented although used at many places
and not present on other OSes (some have other limits).

While surely nobody wants any program to sleep such a long time, it
makes a difference when the code contains calculation underflows
like sleep(x - y) and a negative value is indeed used to sleep
for (almost) ever.

>How-To-Repeat:

$ ./sleep_test -1
sleep(4294967295)
sleep: Invalid argument
returned 32 seconds earlier than requested

$ /bin/sleep 1000000001
sleep: nanosleep failed: Invalid argument

--<cut here>--
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
        unsigned int d;

        d = argv[1] != NULL ? atol(argv[1]) : 0;
        printf("sleep(%u)\n", d);
        if ((d = sleep(d)) != 0)
                perror("sleep");
        printf("returned %d seconds earlier than requested\n", d);
        return 0;
}
--<cut here>--

>Fix:

Either fix the manpages to document the limit (at least) or fix
the implementation:

Index: sleep.c
===================================================================
RCS file: /cvsroot/src/lib/libc/gen/sleep.c,v
retrieving revision 1.21
diff -u -r1.21 sleep.c
--- sleep.c	2000/01/22 22:19:12	1.21
+++ sleep.c	2003/12/01 21:07:54
@@ -54,11 +54,27 @@
 	unsigned int seconds;
 {
 	struct timespec rqt, rmt;
+	unsigned int hi;
 
+	if (seconds > 1000000000L) {
+		hi = seconds / 1000000000L * 1000000000L;
+		seconds -= hi;
+	} else
+		hi = 0;
+
 	rqt.tv_sec  = seconds;
 	rqt.tv_nsec = 0;
+
+	for(;;) {
+		if (nanosleep(&rqt, &rmt) == -1)
+			return hi + (unsigned int)rmt.tv_sec;
 
-	nanosleep(&rqt, &rmt);
+		if (hi) {
+			rqt.tv_sec = 1000000000L;
+			hi -= 1000000000L;
+		} else
+			break;
+	}
 
 	return (unsigned int)rmt.tv_sec;
 }
>Release-Note:
>Audit-Trail:
>Unformatted: