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

Bug#42343: libc6: gethostbyaddr() can hang indefinitely



Package: libc6
Version: 2.1.2-0pre2
Severity: important

This bug was not present in 2.0, but is in 2.1 and up.

The resolver routines that glibc uses use select() internally. If the
select() gets interrupted, it gets restarted, but the timeout value
is re-initialized. If you have a timer in your program that uses
signal(SIGALRM)/alarm() and it runs more often than the select timeout,
gethostbyaddr() for example will hang forever if a DNS packet gets lost.

How to reproduce:

- compile the following sample program
- add a bogus route to your nameserver, temporarily sabotaging name
  lookup. My nameserver is at 195.64.65.25, so I did:
  # route add -host 195.64.65.25 dev lo
- start the following sample program with an IP number as the first
  argument, for example ./a.out 195.64.65.25.
- You can now delete the sabotaged route with
  # route del 195.64.65.25
- the sample program will hang forever in select():

(gdb) where
#0  0x400ab9ae in select () from /lib/libc.so.6
#1  0x400ece18 in __DTOR_END__ () from /lib/libc.so.6
#2  0x400aa38f in poll () from /lib/libc.so.6
#3  0x40107739 in __res_send () from /lib/libresolv.so.2
#4  0x4010687f in res_query () from /lib/libresolv.so.2
#5  0x400fc080 in _nss_dns_gethostbyaddr_r () from /lib/libnss_dns.so.2
#6  0x400c18a2 in gethostbyaddr_r () from /lib/libc.so.6
#7  0x400c1688 in gethostbyaddr () from /lib/libc.so.6
#8  0x804863d in main ()

This is really a problem since for example rpc.mountd hangs at least
once a day on all our NFS serving Linux boxes that run glibc 2.1 and up.

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

void alrm_handler(int sig)
{
	alarm(2);
}

int main(int argc, char **argv)
{
	struct in_addr		a;
	struct hostent		*h;
	struct sigaction	sa;
	
	sa.sa_handler = alrm_handler;
	sa.sa_flags   = 0;
	sigemptyset(&sa.sa_mask);
	sigaction(SIGALRM, &sa, NULL);
	alarm(2);

	if (inet_aton(argv[1], &a) == 0) {
		perror(argv[1]);
		exit(1);
	}

	if ((h = gethostbyaddr(&a, sizeof(a), AF_INET)) == NULL) {
		herror(argv[1]);
		exit(1);
	}

	printf("%s\n", h->h_name);

	return 0;
}

Mike.
-- 
... somehow I have a feeling the hurting hasn't even begun yet
	-- Bill, "The Terrible Thunderlizards"


Reply to: