From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 6863 invoked by alias); 11 Dec 2008 08:42:17 -0000 Received: (qmail 6545 invoked by uid 48); 11 Dec 2008 08:41:04 -0000 Date: Thu, 11 Dec 2008 08:42:00 -0000 From: "pavel dot smolenskiy at gmail dot com" To: glibc-bugs@sources.redhat.com Message-ID: <20081211084103.7094.pavel.smolenskiy@gmail.com> Reply-To: sourceware-bugzilla@sourceware.org Subject: [Bug nptl/7094] New: Bug in creating/deleting posix per process timers with SIGEV_THREAD notification method X-Bugzilla-Reason: CC Mailing-List: contact glibc-bugs-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Post: List-Help: , Sender: glibc-bugs-owner@sourceware.org X-SW-Source: 2008-12/txt/msg00068.txt.bz2 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 #include #include #include #include #include #include #include #include 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=) 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.