This is the mail archive of the
libc-help@sourceware.org
mailing list for the glibc project.
mmap'ed robust mutexes and possible undefined behaviour
- From: Marcos Dione <mdione at grulic dot org dot ar>
- To: libc-help at sourceware dot org
- Date: Mon, 24 Nov 2014 21:34:41 +0100
- Subject: mmap'ed robust mutexes and possible undefined behaviour
- Authentication-results: sourceware.org; auth=none
Hello everybody. First of all, I'm not subscribed to the mailing
list, so please CC me any responses.
We found a situation where a robust mutex cannot be recovered
from a stale lock and we're wondering if it's simply an undefined
situation or a bug in the kernel. Attached you will find the sample
code, which is loosely based on a glibc's test case.The gist of it is as
follows:
1. we open a file.
2. we mmap it and use that mem to store a robust mutex.
3. we lock the mutex.
4. we munmap the file.
5. we close the file.
The example does steps 1 and 2, then creates creates tw children
who will try to do steps 3 to 5. Of course only one gets the lock while
the other waits. If the child who has the lock does the 4th step, then
the other child never recovers the stale lock. In any other situation
(that is, commenting/removing the code) it works fine.
This looks suspiciously like undefined behaviour, because it's like
we're pulling the rug from under the mutex' feet, but in the other hand
looks like a kernel bug because it doesn't really recover from the
situation. What do you think?
--
(Not so) Random fortune:
22:22 < m4rgin4l_> hendrix no tocaba la viola, violaba la toca
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
void *tf (int n, int f, pthread_mutex_t *m) {
int err = pthread_mutex_lock (m);
printf ("ml: %d\n", err);
if (err == EOWNERDEAD) {
err = pthread_mutex_consistent_np (m);
printf ("mc: %d\n", err);
if (err) {
puts ("pthread_mutex_consistent_np");
exit (1);
}
} else if (err) {
puts ("pthread_mutex_lock");
exit (1);
}
printf ("%ld got the lock.\n", n);
sleep (3);
/* if this munmap() call is commented, the oher child can recover the lock */
munmap (m, sizeof (pthread_mutex_t));
close (f);
printf ("%ld out\n", n);
return NULL;
}
int main (void) {
int err, f;
pthread_mutex_t *m;
pthread_mutexattr_t ma;
pthread_mutexattr_init (&ma);
err = pthread_mutexattr_setrobust_np (&ma, PTHREAD_MUTEX_ROBUST_NP);
if (err) {
puts ("pthread_mutexattr_setrobust_np");
return 1;
}
err = pthread_mutexattr_setpshared (&ma, PTHREAD_PROCESS_SHARED);
if (err) {
puts ("pthread_mutexattr_setpshared");
return 1;
}
#ifdef ENABLE_PI
if (pthread_mutexattr_setprotocol (&ma, PTHREAD_PRIO_INHERIT) != 0) {
puts ("pthread_mutexattr_setprotocol failed");
return 1;
}
#endif
f= open ("mutex.mmap", O_CREAT|O_TRUNC|O_RDWR);
if (f<0) {
puts ("open");
return 1;
}
err= ftruncate (f, sizeof (pthread_mutex_t));
if (err) {
puts ("ftruncate");
return 1;
}
m= (pthread_mutex_t *) mmap (NULL, sizeof (pthread_mutex_t),
PROT_READ|PROT_WRITE, MAP_SHARED, f, 0);
err = pthread_mutex_init (m, &ma);
#ifdef ENABLE_PI
if (err == ENOTSUP) {
puts ("PI robust mutexes not supported");
return 0;
}
#endif
if (err) {
puts ("pthread_mutex_init");
return 1;
}
err= fork ();
if (err==0) {
tf (1, f, m);
} else if (err>0) {
int err2= fork ();
if (err2==0) {
tf (2, f, m);
} else if (err2>0) {
// sleep (1);
// kill (err);
// printf ("child killed\n");
// sleep (10);
puts ("main out");
} else {
puts ("fork2");
return 1;
}
} else {
puts ("fork1");
return 1;
}
return 0;
}