This is the mail archive of the glibc-bugs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug nptl/7094] New: Bug in creating/deleting posix per process timers with SIGEV_THREAD notification method


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.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]