This is the mail archive of the
glibc-bugs@sourceware.org
mailing list for the glibc project.
[Bug nptl/7094] New: Bug in creating/deleting posix per process timers with SIGEV_THREAD notification method
- From: "pavel dot smolenskiy at gmail dot com" <sourceware-bugzilla at sourceware dot org>
- To: glibc-bugs at sources dot redhat dot com
- Date: 11 Dec 2008 08:41:04 -0000
- Subject: [Bug nptl/7094] New: Bug in creating/deleting posix per process timers with SIGEV_THREAD notification method
- Reply-to: sourceware-bugzilla at sourceware dot org
Issue is following.
1. Create x timers, where x>1, calling timer_create, with appropriate arguments,
and SIGEV_THREAD as notification method on expire.
2. Delete last created timer with timer_delete before it's expire time.
3. Wait for another timer to expire. On attempt to call notification function
glibc hang with SIGSEGV in timer_helper_thread().
Tested on glibc 2.4, 2.7, 2.8, 2.9 and latest cvs head with ubuntu 8.10 on amd64.
Here is test code
[code]
/* file timer_t.c */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
void timer_expire_func(union sigval arg)
{
void *obj = arg.sival_ptr;
printf("%s(): called: %p\n", __FUNCTION__, obj);
}
pthread_mutex_t a = PTHREAD_MUTEX_INITIALIZER;
int q = 0;
void *thr_func(void *arg)
{
int qf = 0;
while(1) {
pthread_mutex_lock(&a);
qf = q;
pthread_mutex_unlock(&a);
if (qf)
break;
sleep(2);
}
return NULL;
}
timer_t t1, t2;
int main(void)
{
struct sigevent sigev;
struct timeval expire;
struct itimerval exp;
sigev.sigev_notify = SIGEV_THREAD;
sigev.sigev_notify_function = timer_expire_func;
sigev.sigev_notify_attributes = 0;
sigev.sigev_value.sival_ptr = (void *) &t1;
expire.tv_sec = 10;
expire.tv_usec = 0;
if (timer_create(CLOCK_REALTIME, &sigev, &t1) ) {
perror("timer_create");
exit(-1);
}
exp.it_value.tv_sec = expire.tv_sec;
exp.it_value.tv_usec = expire.tv_usec;
exp.it_interval.tv_sec = 0;
exp.it_interval.tv_usec = 0;
printf("add timer: %p, exp(s,u) = (%lu, %lu)\n", t1,
expire.tv_sec, expire.tv_usec);
timer_settime(t1, 0, (const struct itimerspec*)&exp, NULL);
memset(&sigev, 0, sizeof(sigev));
sigev.sigev_notify = SIGEV_THREAD;
sigev.sigev_notify_function = timer_expire_func;
sigev.sigev_notify_attributes = 0;
sigev.sigev_value.sival_ptr = (void *) &t2;
expire.tv_sec = 10;
expire.tv_usec = 0;
if (timer_create(CLOCK_REALTIME, &sigev, &t2) ) {
perror("timer_create");
exit(-1);
}
exp.it_value.tv_sec = expire.tv_sec;
exp.it_value.tv_usec = expire.tv_usec;
exp.it_interval.tv_sec = 0;
exp.it_interval.tv_usec = 0;
printf("add timer: %p, exp(s,u) = (%lu, %lu)\n", t2,
expire.tv_sec, expire.tv_usec);
timer_settime(t2, 0, (const struct itimerspec*)&exp, NULL);
pthread_t ptid;
pthread_create(&ptid, NULL, thr_func, NULL);
timer_delete(t2);
int rr;
pthread_join(ptid, (void**)&rr);
exit(0);
[/code]
# gcc -Wall -g -o timer_t timer_t.c -lrt
# gdb ./timer_t
(gdb) r
Starting program: /tmp/timer_t
[Thread debugging using libthread_db enabled]
[New Thread 0x7fd8ae7e26e0 (LWP 25091)]
[New Thread 0x41006950 (LWP 25092)]
add timer: 0xd4c130, exp(s,u) = (10, 0)
add timer: 0xd4c190, exp(s,u) = (10, 0)
[New Thread 0x42260950 (LWP 25093)]
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x41006950 (LWP 25092)]
timer_helper_thread (arg=<value optimized out>) at
../nptl/sysdeps/unix/sysv/linux/timer_routines.c:114
114 runp = runp->next;
(gdb) p runp
$1 = (struct timer *) 0x60
(gdb)
======
So I look up into the code and found out, that SIGEV_THREAD timers, on creation,
stored in one-way linked list, last created timer is in the head.
Then, when delete timer, glibc found that timer in linked list and delete it
from there.
nptl/sysdeps/unix/sysv/linux/timer_delete.c:42
timer_delete (timerid)
timer_t timerid;
{
# undef timer_delete
# ifndef __ASSUME_POSIX_TIMERS
if (__no_posix_timers >= 0)
# endif
{
struct timer *kt = (struct timer *) timerid;
/* Delete the kernel timer object. */
int res = INLINE_SYSCALL (timer_delete, 1, kt->ktimerid);
if (res == 0)
{
if (kt->sigev_notify == SIGEV_THREAD)
{
/* Remove the timer from the list. */
skip
skip
skip
}
# ifndef __ASSUME_POSIX_TIMERS
/* We know the syscall support is available. */
__no_posix_timers = 1;
# endif
/* Free the memory. */
(void) free (kt);
return 0;
}
But when look into
nptl/sysdeps/unix/sysv/linux/timer_create.c:52
timer_create (clock_id, evp, timerid)
clockid_t clock_id;
struct sigevent *evp;
timer_t *timerid;
{
/* skip non SIGEV_THREAD code */
line 162
struct timer *newp;
newp = (struct timer *) malloc (sizeof (struct timer));
if (newp == NULL)
return -1;
/* Copy the thread parameters the user provided. */
newp->sival = evp->sigev_value;
newp->thrfunc = evp->sigev_notify_function;
skipped
line 207
if (! INTERNAL_SYSCALL_ERROR_P (res, err))
{
/* Add to the queue of active timers with thread
delivery. */
pthread_mutex_lock (&__active_timer_sigev_thread_lock);
newp->next = __active_timer_sigev_thread;
__active_timer_sigev_thread = newp;
pthread_mutex_unlock (&__active_timer_sigev_thread_lock);
*timerid = (timer_t) newp;
return 0;
}
}
Nobody set newp->sigev_notify = SIGEV_THREAD.
So, when call timer_delete(), kt->sigev_notify value not specified, thus lead to
simple free kt pointer, w/o deleting it from linked list prior.
So, when deleting last created timer, linked list simply become lost, cause head
was free'd, and next expired timer will hang glibc.
--
Summary: Bug in creating/deleting posix per process timers with
SIGEV_THREAD notification method
Product: glibc
Version: unspecified
Status: NEW
Severity: normal
Priority: P2
Component: nptl
AssignedTo: drepper at redhat dot com
ReportedBy: pavel dot smolenskiy at gmail dot com
CC: glibc-bugs at sources dot redhat dot com
GCC build triplet: all
GCC host triplet: All
GCC target triplet: all
http://sourceware.org/bugzilla/show_bug.cgi?id=7094
------- You are receiving this mail because: -------
You are on the CC list for the bug, or are watching someone who is.