This is the mail archive of the
libc-alpha@sourceware.cygnus.com
mailing list for the glibc project.
[artdodge@cs.bu.edu] libc/1220: The code in resolv/res_send.c is not thread-safe
- To: libc-alpha Mailinglist <libc-alpha@sourceware.cygnus.com>
- Subject: [artdodge@cs.bu.edu] libc/1220: The code in resolv/res_send.c is not thread-safe
- From: Andreas Jaeger <aj@arthur.rhein-neckar.de>
- Date: 25 Jul 1999 00:46:47 +0200
- Cc: artdodge@cs.bu.edu
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
- Subject: libc/1220: The code in resolv/res_send.c is not thread-safe
- From: artdodge@cs.bu.edu
- Date: Sun Jul 25 00:44:49 1999
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