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.