threads/112300: Memory leak in free_tls within ld-elf.so

Spencer Minear minear at securecomputing.com
Tue May 1 13:10:05 UTC 2007


>Number:         112300
>Category:       threads
>Synopsis:       Memory leak in free_tls within ld-elf.so
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-threads
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue May 01 13:10:05 GMT 2007
>Closed-Date:
>Last-Modified:
>Originator:     Spencer Minear
>Release:        6.0
>Organization:
Secure Computing Corp
>Environment:
Found running our modified 6.0 code, but observed on 6.0 and code inspection suggests it is present on Current.
>Description:
The allocate_tls function in libexec/rtld-elf/rtld.c allocates two memory areas with each call.  The free_tls function in the same file only free's one of them wich each call.  I.E. there is a memory leak with each thread distruction.  The problem gets rather serious for applications like a mail filter based on libmilter.

We found the problem with our modified 6.0 based system but it code has not changed even on current so I think it is present in all current released versions fo the code.

A static link does not leak, which lead us to the solution in that the code for the same functions in libc (gen/tls.c) does not have the problem.  The problem was observed when we used both libpthread and libthr, thus pointin us to the ld-elf code.

>How-To-Repeat:
Run a program that creates many short lived threads. Following test program shows the problem very quickly.


#include <sys/types.h>
#include <time.h>
#include <sys/time.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>

#define INFO_FILE	"thread_test_file"

#define CREATE_THREAD(ret, func, arg) \
	{ \
		pthread_t tid; \
		if ((ret = pthread_create(&tid, NULL, func, (void*)(arg))) == 0) \
			ret = pthread_detach(tid); \
	}

static void*
mem_thread(void *arg)
{
    int fd;
    FILE *fp;
    pthread_t tid;

    tid = pthread_self();

    if ((fd = open(INFO_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644)) 
   	< 0) {
	fprintf(stderr, "Error opening %s: %s\n", INFO_FILE, strerror(errno));
	return(NULL);
    }

    printf("File %s created\n", INFO_FILE);

    if ((fp = fdopen(fd, "w")) == NULL) {
	printf("Can't fdopen fd %d, %s\n", 
	       fd, strerror(errno));
	close(fd);
	return(NULL);
    }
    fprintf(fp, "TEST of writing to file");
    fflush(fp);
    fclose(fp);
    close(fd);

    return(NULL);
}

int
main(int argc, char **argv, char **envp)
{
    int ret;
    struct timespec rqtp;
    int i = 0;

    printf("Starting\n");

    while (1) {

	CREATE_THREAD(ret, &mem_thread, NULL);
	if (ret) {
	    perror("Creating thread1");
	    return -1;
	}
        rqtp.tv_sec = 0;
	rqtp.tv_nsec = 1000 * (i % 25);
	
	nanosleep(&rqtp, NULL);
	i++;
    }

    return 0;
}

>Fix:
Copy the logic in the libc/gen/tls.c code and add a call to free the dtv pointer in free_tls within rtld.c.

The Diff within in our code base CVS tree is as follows.  This seems to fix the problem.  I trust you will let us know if we are overlooking something and this fix is not safe.

--- rtld.c      3 Jan 2006 20:44:27 -0000       1.4
+++ rtld.c      1 May 2007 12:37:45 -0000
@@ -2733,6 +2733,7 @@
     }
 
     free((void*) tlsstart);
+    free(dtv);
 }
 
 #endif


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-threads mailing list