misc/149366: pthread_cleanup_pop never runs the configured routine
Fernando Lemos
fernando at netfilter.com.br
Fri Aug 6 14:50:03 UTC 2010
>Number: 149366
>Category: misc
>Synopsis: pthread_cleanup_pop never runs the configured routine
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Fri Aug 06 14:50:02 UTC 2010
>Closed-Date:
>Last-Modified:
>Originator: Fernando Lemos
>Release: 7.2-p8
>Organization:
NetFilter
>Environment:
FreeBSD iron.netfilter.com.br 7.2-RELEASE-p8 FreeBSD 7.2-RELEASE-p8 #0: Wed May 26 03:08:50 UTC 2010 root at i386-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC i386
>Description:
The pthread_cleanup_pop docs say:
"The pthread_cleanup_pop() function shall remove the routine at the top of the calling thread's cancellation cleanup stack and optionally invoke it (if execute is non-zero)."
However, in the attached sample, we invoke pthread_cleanup_pop with execute non-zero and still the routine isn't called.
I got to reproduce it consistently in FreeBSD 7.2 and also in one FreeBSD 7.0-PRERELEASE box, so I think it might even affect newer versions (I don't have access to a box running 7.3 or 8.x).
If pthread_cleanup_pop doesn't work properly, all sorts of deadlocks and races are possible, not to mention resource leaks, so this is kind of important (although I don't think pthread_cleanup_pop is widely used).
TIA,
>How-To-Repeat:
The attached sample C file can be used. To compile it, run "gcc -O0 -ggdb -o test main.c", for example.
Run it with "gdb test". Break on release_update_lock. You'll see that it never gets called.
Compare the outputs, first a Linux machine:
Init
Res: 0
Locking read
Res: 0
Unlocking
Res: 0
Locking write
Res: 0
Unlocking
Res: 0
Locking read
Res: 0
Unlocking with cleanup pop
release_update_rwlock: 0
Locking write
Res: 0
Unlocking
Res: 0
Now a FreeBSD 7.2-p8 machine:
Init
Res: 0
Locking read
Res: 0
Unlocking
Res: 0
Locking write
Res: 0
Unlocking
Res: 0
Locking read
Res: 0
Unlocking with cleanup pop
Locking write
Res: 0
Unlocking
Res: 0
Note how release_update_rwlock is never called (and gdb confirms it).
>Fix:
Patch attached with submission follows:
#include <pthread.h>
#include <stdio.h>
static pthread_rwlock_t trava;
static void release_update_rwlock(void *dummy)
{
int res = pthread_rwlock_unlock(&trava);
printf("release_update_rwlock: %d\n", res);
}
int main(int argc, char **argv)
{
printf("Init\n");
int res = pthread_rwlock_init(&trava, NULL);
printf("Res: %d\n", res);
printf("Locking read\n");
res = pthread_rwlock_rdlock(&trava);
printf("Res: %d\n", res);
printf("Unlocking\n");
res = pthread_rwlock_unlock(&trava);
printf("Res: %d\n", res);
printf("Locking write\n");
res = pthread_rwlock_wrlock(&trava);
printf("Res: %d\n", res);
printf("Unlocking\n");
res = pthread_rwlock_unlock(&trava);
printf("Res: %d\n", res);
printf("Locking read\n");
res = pthread_rwlock_rdlock(&trava);
printf("Res: %d\n", res);
printf("Unlocking with cleanup pop\n");
pthread_cleanup_push(&release_update_rwlock, NULL);
pthread_cleanup_pop(1);
printf("Locking write\n");
res = pthread_rwlock_wrlock(&trava);
printf("Res: %d\n", res);
printf("Unlocking\n");
res = pthread_rwlock_unlock(&trava);
printf("Res: %d\n", res);
return 0;
}
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list