From nobody@FreeBSD.org  Thu Aug 30 15:37:42 2007
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 0CB6016A41A
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 30 Aug 2007 15:37:42 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21])
	by mx1.freebsd.org (Postfix) with ESMTP id F0E6413C4B3
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 30 Aug 2007 15:37:41 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.14.1/8.14.1) with ESMTP id l7UFbfW2038615
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 30 Aug 2007 15:37:41 GMT
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.14.1/8.14.1/Submit) id l7UFbfYj038614;
	Thu, 30 Aug 2007 15:37:41 GMT
	(envelope-from nobody)
Message-Id: <200708301537.l7UFbfYj038614@www.freebsd.org>
Date: Thu, 30 Aug 2007 15:37:41 GMT
From: Oleg <Oleg.Dolgov@gmail.com>
To: freebsd-gnats-submit@FreeBSD.org
Subject: [libpam] not thread-safe
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         115946
>Category:       bin
>Synopsis:       [libpam] [patch] not thread-safe
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    des
>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:  Tue Sep  4 09:00:07 GMT 2007
>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:
Responsible-Changed-From-To: freebsd-bugs->des 
Responsible-Changed-By: remko 
Responsible-Changed-When: Thu Aug 30 16:12:42 UTC 2007 
Responsible-Changed-Why:  
over to pam maintainer 

http://www.freebsd.org/cgi/query-pr.cgi?pr=115946 

From: =?utf-8?Q?Dag-Erling_Sm=C3=B8rgrav?= <des@des.no>
To: Oleg <Oleg.Dolgov@gmail.com>
Cc: freebsd-gnats-submit@FreeBSD.org
Subject: Re: bin/115946: [libpam] not thread-safe
Date: Tue, 04 Sep 2007 10:56:40 +0200

 Oleg <Oleg.Dolgov@gmail.com> writes:
 > Add synchronization code around access to global variable
 > 'modules' in /usr/src/contrib/openpam/lib/openpam_load.c.
 
 It's probably better to simply remove it.  It serves no real purpose.
 
 DES
 --=20
 Dag-Erling Sm=C3=B8rgrav - des@des.no
>Unformatted:
