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