Date: Thu, 30 Aug 2007 15:37:41 GMT From: Oleg <Oleg.Dolgov@gmail.com> To: freebsd-gnats-submit@FreeBSD.org Subject: bin/115946: [libpam] not thread-safe Message-ID: <200708301537.l7UFbfYj038614@www.freebsd.org> Resent-Message-ID: <200708301540.l7UFe1RZ043201@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 115946 >Category: bin >Synopsis: [libpam] not thread-safe >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Thu Aug 30 15:40:01 GMT 2007 >Closed-Date: >Last-Modified: >Originator: Oleg >Release: 6.2-RELEASE 7-CURRENT >Organization: Sunbay >Environment: FreeBSD netsnapper.domain.com 6.2-RELEASE FreeBSD 6.2-RELEASE #0: Tue Aug 14 15:14:19 EEST 2007 root@netsnapper.domain.com:/usr/src/sys/i386/compile/NETSNAPPER i386 (SMP + SCHED_ULE) >Description: openpam_load_module/openpam_release_module (/usr/src/contrib/openpam/lib/openpam_load.c) called from pam_start/pam_end not thread safe. They use global static variable for list of previously found modules. Easly reproduceable with attached program (only on true 2-x CPU machine). >How-To-Repeat: Steps: # cc -Wall -D_UNIX -D_DEBUG -std=c99 -I/usr/local/include -I/usr/include/security -O0 -g -fno-inline -L/usr/local/lib -o pthreads_and_pam main.c -Xlinker -Bdynamic -lpam -lpthread # echo "auth required pam_permit.so" >/usr/local/etc/pam.d/pthreads_and_pam # ./pthreads_and_pam .................... (after 4 sec - 5 min) pthreads_and_pam in free(): error: chunk is already free Abort trap: 6 (core dumped) # grep openpam /var/log/messages Aug 30 17:33:57 netsnapper pthreads_and_pam: in openpam_release_module(): module (null) has negative refcount >Fix: Add synchronization code around access to global variable 'modules' in /usr/src/contrib/openpam/lib/openpam_load.c. Can take from /usr/src/libexec/rtld-elf/rtld_lock.c or add new library depend from libpthread and use their function (ugly?) Patch attached with submission follows: #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <unistd.h> #include <pthread.h> #include <pam_appl.h> typedef struct _auth_subject_t { const char *username; const char *password; const char *service; } auth_subject_t; #define NUM_THREADS 50 volatile int num_threads; pthread_mutex_t num_thr_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t last_thread_fini; int pam_auth_conv(int NumOfMsgs, const struct pam_message **pMsgs, struct pam_response **pResponces, void *pContext) { int i = 0; *pResponces = (struct pam_response *) malloc(NumOfMsgs * sizeof(struct pam_response)); if (*pResponces == NULL) { return 0; } bzero(*pResponces, NumOfMsgs * sizeof(struct pam_response)); for (i = 0; i < NumOfMsgs; i++) { switch(pMsgs[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: (*pResponces)[i].resp = strdup(((auth_subject_t*)pContext)->password); (*pResponces)[i].resp_retcode = 0; break; case PAM_PROMPT_ECHO_ON: (*pResponces)[i].resp = strdup(((auth_subject_t*)pContext)->username); (*pResponces)[i].resp_retcode = 0; break; case PAM_ERROR_MSG: break; case PAM_TEXT_INFO: break; default: break; } } return PAM_SUCCESS; } void* test(void* arg) { struct pam_conv conv = {0}; auth_subject_t auth_subject = { /*login*/"agile", /*password*/"agile", "pthreads_and_pam" }; conv.conv = pam_auth_conv; conv.appdata_ptr = &auth_subject; int result = 0; pam_handle_t *pam_handle = NULL; const char *pam_service_name = auth_subject.service; result = pam_start(pam_service_name, NULL, &conv, &pam_handle); if (result != PAM_SUCCESS) { fprintf(stderr, "pam_start failed, %s\n", pam_strerror(pam_handle, result)); goto failed; } result = pam_authenticate(pam_handle, 0); if (result != PAM_SUCCESS) { fprintf(stderr, "* %s\n", pam_strerror(pam_handle, result)); goto failed; } putc('.', stderr); failed: if (pam_handle) { pam_end(pam_handle, 0); } pthread_mutex_lock(&num_thr_mutex); num_threads--; // last thread? if (num_threads == 0) { pthread_cond_signal(&last_thread_fini); } pthread_mutex_unlock(&num_thr_mutex); return NULL; } int main(int argc, char *argv[]) { pthread_cond_init(&last_thread_fini, NULL); pthread_t thr; //for(int x=0; x<1; x++) while(1) { pthread_mutex_lock(&num_thr_mutex); num_threads = NUM_THREADS; for (int i=0; i<num_threads; i++) { if (pthread_create(&thr, NULL, test, (void*)i+1)) { fprintf(stderr, "can't create thread"); } else { pthread_detach(thr); } } // wait pthread_cond_wait(&last_thread_fini, &num_thr_mutex); putc('\n', stderr); pthread_mutex_unlock(&num_thr_mutex); } return 0; } >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200708301537.l7UFbfYj038614>