This is the mail archive of the libc-alpha@sourceware.cygnus.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

[artdodge@cs.bu.edu] libc/1220: The code in resolv/res_send.c is not thread-safe



We've got the appended bug report about res_send.c not being
thread-safe.

I'm appending the test program since it's small.

Could anybody look into it?

Thanks,
Andreas



Topics:
   libc/1220: The code in resolv/res_send.c is not thread-safe


----------------------------------------------------------------------

Date: Sat, 24 Jul 1999 18:10:39 -0400
From: artdodge@cs.bu.edu
To: bugs@gnu.org
Subject: libc/1220: The code in resolv/res_send.c is not thread-safe
Message-Id: <199907242210.SAA14334@delysid.gnu.org>


>Number:         1220
>Category:       libc
>Synopsis:       The code in resolv/res_send.c is not thread-safe
>Confidential:   no
>Severity:       critical
>Priority:       medium
>Responsible:    libc-gnats
>State:          open
>Class:          sw-bug
>Submitter-Id:   unknown
>Arrival-Date:   Sat Jul 24 18:20:01 EDT 1999
>Last-Modified:
>Originator:     artdodge@cs.bu.edu
>Organization:
net
>Release:        2.0.ALL, 2.1.1
>Environment:
Redhat 5.0, 5.1, 5.2, 6.0; several glibc2.0 versions as well as glibc2.1.1
>Description:
A race condition in resolv/res_send.c causes multi-threaded programs which
use gethostbyname_r() and similar functions to hang indefinitely in recvfrom().
The race condition involves the use of the file-wide "int s" variable, which
appears to describe a single socket that should be used to resolve all queries.  
As a result of the race, threads will open sockets then have their reference
(the value of s) replaced by another thread, causing them to subsequently
either poll() the wrong socket, or poll() correctly but then sleep while trying
to recvfrom() on the wrong socket.
>How-To-Repeat:
Rob Riggs <rob@pangalactic.org> concocted a straightforward test case:
ftp://ftp.pangalactic.org/pub/outgoing/thrtest.c%0
>Fix:
>Audit-Trail:
>Unformatted:


------------------------------

End of forwardqKBQuP Digest
***************************



/*
 * thrtest.c
 *
 * Linux kernel sock_select() break-it code
 * Copyright (c) 1999 Tummy.com, Ltd. All rights reserved.
 * Written by Rob Riggs - Released under the Gnu GPL
 *
 * When used properly, this program may cause systems to behave
 * erratically. You have been warned.
 * 
 * make LDFLAGS=-lpthread thrtest
 */

#include <unistd.h>
#include <stdio.h>
#include <netdb.h>
#include <syslog.h>
#include <pthread.h>


#define HOSTNAMELEN 256
#define GETHOSTBUFLEN 8192
#define LOG_NAME "thrtest"

/* 
 * Note: gethostbyname_r() must use DNS, or it will not
 * illustrate the problem. 
 */

/* #define MYHOST 1 */

#ifdef MYHOST
#define HOSTNAME thread->hostname
#else
#define HOSTNAME "tummy.com"
#endif

void log(char *msg)
{
	openlog(LOG_NAME, 0, LOG_LOCAL3);
	syslog(LOG_NOTICE, msg);
	closelog();
	return;
}

typedef struct {
	pthread_mutex_t		lock;
	pthread_t			thread;
	int					alive;
	char				hostname[HOSTNAMELEN];
} thread_data_t;

int thread_init(thread_data_t **thread)
{
	int				status;
	
	*thread = (thread_data_t *) malloc(sizeof(thread_data_t));
	if (*thread == NULL)
		return -1;
	memset(*thread, 0, sizeof(thread_data_t));
	pthread_mutex_init(&((*thread)->lock), NULL);
	(*thread)->alive = 0;
	status = gethostname((*thread)->hostname, HOSTNAMELEN);
	if (status < 0)
		return h_errno;
	else
		return status;
}

int thread_delete(thread_data_t **thread, int index, int max)
{
	void				*status;
	pthread_join(thread[index]->thread, &status);
	free(*(thread+index));
	memcpy(thread + index, thread + index + 1, (max - (index +1)) * sizeof(thread_data_t *));
}

void thread_main(thread_data_t *thread)
{
	struct hostent	result, *hp;
	char			*buf;
	int				status, err;
	unsigned char	ipaddr[4], logbuf[256];
	struct timespec		req, rem;
	
	buf = (char *) malloc(GETHOSTBUFLEN);
	if (buf == NULL)
		return;
	status = gethostbyname_r(HOSTNAME, &result, buf, GETHOSTBUFLEN, &hp, &err);
	if (hp == NULL)
		return;
	memcpy(ipaddr, hp->h_addr, hp->h_length);
	sprintf(logbuf, "%s is %d.%d.%d.%d", HOSTNAME, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
	log(logbuf);
	return;
}

void *thread_run(void *data)
{
	thread_data_t	*thread;
	
	thread = (thread_data_t *) data;
	thread->alive = 1;
	pthread_mutex_unlock(&thread->lock);
	thread_main(thread);
	pthread_mutex_lock(&thread->lock);
	thread->alive = 0;
	pthread_mutex_unlock(&thread->lock);
	return NULL;	
}

int thread_start(thread_data_t *thread)
{
	int				status;
	
	pthread_mutex_lock(&thread->lock);
	status = pthread_create(&(thread->thread), NULL, thread_run, thread);
	return status;
}

int isAlive(thread_data_t *thread)
{
	int		val;

	pthread_mutex_lock(&(thread->lock));
	val = thread->alive;
	pthread_mutex_unlock(&(thread->lock));
	return val;
}

#define MAXTHREADS 50

int main(int argc, char *argv[])
{
	thread_data_t		*threads[MAXTHREADS];
	int					clients = 0, index = 0, i, status;
	struct timespec		req, rem;
	
	for (i = 0; i < 1000; i++) {

		/* Cull finished threads */
		index = 0;
		while (index < clients) {
			if (isAlive(threads[index]))
				index++;
			else {
				thread_delete(threads, index, MAXTHREADS);
				clients--;
			}
			if ((index == clients) && (clients >= MAXTHREADS)) {
				index = 0;
				req.tv_sec = 0;
				req.tv_nsec = 100000000;
				nanosleep(&req, &rem);
			}
		}

		if (thread_init(&threads[clients]) != 0) {
			printf("error initializing threads\n");
			break;
		}
		while (1) {
			status = thread_start(threads[clients]);
			if (status == 0)
				break;
			req.tv_sec = 0;
			req.tv_nsec = 100000000;
			nanosleep(&req, &rem);
		}
		clients++;
	}
	index = 0;
	while (clients > 0) {
		if (index >= clients) {
			index = 0;
			req.tv_sec = 0;
			req.tv_nsec = 100000000;
			nanosleep(&req, &rem);
		}
		if (isAlive(threads[index]))
			index++;
		else {
			thread_delete(threads, index, MAXTHREADS);
			clients--;
		}
	}

	return 0;
}

-- 
 Andreas Jaeger   aj@arthur.rhein-neckar.de    jaeger@informatik.uni-kl.de
  for pgp-key finger ajaeger@aixd1.rhrk.uni-kl.de

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]