bin/115946: [libpam] not thread-safe
Oleg
Oleg.Dolgov at gmail.com
Thu Aug 30 08:40:02 PDT 2007
>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 at 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:
More information about the freebsd-bugs
mailing list