Subject: lib/6410: res_send() timeout bug
To: None <gnats-bugs@gnats.netbsd.org>
From: maximum entropy <entropy@venom.bernstein.com>
List: netbsd-bugs
Date: 11/08/1998 05:31:06
>Number:         6410
>Category:       lib
>Synopsis:       res_send() timeout bug
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    lib-bug-people (Library Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Nov  8 02:35:01 1998
>Last-Modified:
>Originator:     maximum entropy
>Organization:
	
>Release:        <NetBSD-current source date> 19981030
>Environment:
	
System: NetBSD venom.bernstein.com 1.3H NetBSD 1.3H (VENOM) #0: Fri Oct 30 04:18:28 EST 1998 entropy@venom:/usr/src/sys/arch/i386/compile/VENOM i386
machine: i386
os: NetBSD 1.3H
libs: libc.so.12.33

>Description:
	
If the resolver is using UDP, and the query datagram is lost, a frequently
interrupting timer (or any other frequent signal source) can cause res_send()
to thrash without ever timing out.

>How-To-Repeat:
	
1.  Ensure that /etc/resolv.conf specifies exactly one nameserver.
2.  Add a bogus route to the nameserver, to force a condition where the
	datagrams sent to the server will be lost.  E.g. if the nameserver
	is 1.2.3.4 then:
		# route delete 1.2.3.4 ; route add -host 1.2.3.4 127.0.0.1
3.  Run the following test program, and note that it never terminates:

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#include <stdlib.h>

void
al()
{
}

int
main()
{
   struct sockaddr_in incoming;
   struct hostent *hp;
   struct itimerval new;
   struct itimerval old;
   struct sigaction act;
   struct sigaction oact;

   sigemptyset(&(act.sa_mask));
   act.sa_flags = 0;
   act.sa_handler = al;
   sigaction(SIGALRM, &act, &oact);

   new.it_interval.tv_sec = 0;
   new.it_interval.tv_usec = 1;
   new.it_value.tv_sec = 0;
   new.it_value.tv_usec = 1;

   setitimer(ITIMER_REAL, &new, &old);

   srandom((unsigned) time(0));
   incoming.sin_addr.s_addr = random();
   
   hp = gethostbyaddr((char *) &(incoming.sin_addr.s_addr),
            sizeof(incoming.sin_addr.s_addr),
            AF_INET);
   printf("all done\n");
   return 0;
}
>Fix:
	
Recompute the timeout each time res_send is about to call poll().

--- /usr/src/lib/libc/net/res_send.c_orig	Thu Oct 15 07:07:56 1998
+++ /usr/src/lib/libc/net/res_send.c	Sun Nov  8 05:04:42 1998
@@ -496,6 +496,8 @@
 			 * Use datagrams.
 			 */
 			time_t seconds;
+			time_t start_time;
+			time_t end_time;
 			struct pollfd dsfd;
 			struct sockaddr_in from;
 			int fromlen;
@@ -602,8 +604,14 @@
 				seconds = 1;
 			dsfd.fd = s;
 			dsfd.events = POLLIN;
+			start_time = time(0);
+			end_time = start_time + seconds;
 wait:
+			seconds = end_time - start_time;
+			if (seconds < 0)
+				seconds = 0;
 			n = poll(&dsfd, 1, seconds * 1000);
+			start_time = time(0);
 			if (n < 0) {
 				if (errno == EINTR)
 					goto wait;
>Audit-Trail:
>Unformatted:
Under certain circumstances, res_send() will never terminate.