Author: mav
Date: Wed Mar  6 22:40:47 2013
New Revision: 247903
URL: http://svnweb.freebsd.org/changeset/base/247903

Log:
  Reduce minimal time intervals of setitimer(2) from 1/HZ to 1/(16*HZ) by
  using callout_reset_sbt() instead of callout_reset().  We can't remove
  lower limit completely in this case because of significant processing
  overhead, caused by unability to use direct callout execution due to using
  process mutex in callout handler for sending SEGALRM signal.  With support
  of periodic events that would allow unprivileged user to abuse the system.
  
  Reviewed by:  davide

Modified:
  head/sys/kern/kern_time.c

Modified: head/sys/kern/kern_time.c
==============================================================================
--- head/sys/kern/kern_time.c   Wed Mar  6 22:12:45 2013        (r247902)
+++ head/sys/kern/kern_time.c   Wed Mar  6 22:40:47 2013        (r247903)
@@ -691,7 +691,7 @@ kern_getitimer(struct thread *td, u_int 
                *aitv = p->p_realtimer;
                PROC_UNLOCK(p);
                if (timevalisset(&aitv->it_value)) {
-                       getmicrouptime(&ctv);
+                       microuptime(&ctv);
                        if (timevalcmp(&aitv->it_value, &ctv, <))
                                timevalclear(&aitv->it_value);
                        else
@@ -736,28 +736,33 @@ kern_setitimer(struct thread *td, u_int 
 {
        struct proc *p = td->td_proc;
        struct timeval ctv;
+       sbintime_t sbt, pr;
 
        if (aitv == NULL)
                return (kern_getitimer(td, which, oitv));
 
        if (which > ITIMER_PROF)
                return (EINVAL);
-       if (itimerfix(&aitv->it_value))
+       if (itimerfix(&aitv->it_value) ||
+           aitv->it_value.tv_sec > INT32_MAX / 2)
                return (EINVAL);
        if (!timevalisset(&aitv->it_value))
                timevalclear(&aitv->it_interval);
-       else if (itimerfix(&aitv->it_interval))
+       else if (itimerfix(&aitv->it_interval) ||
+           aitv->it_interval.tv_sec > INT32_MAX / 2)
                return (EINVAL);
 
        if (which == ITIMER_REAL) {
                PROC_LOCK(p);
                if (timevalisset(&p->p_realtimer.it_value))
                        callout_stop(&p->p_itcallout);
-               getmicrouptime(&ctv);
+               microuptime(&ctv);
                if (timevalisset(&aitv->it_value)) {
-                       callout_reset(&p->p_itcallout, tvtohz(&aitv->it_value),
-                           realitexpire, p);
+                       pr = tvtosbt(aitv->it_value) >> tc_precexp;
                        timevaladd(&aitv->it_value, &ctv);
+                       sbt = tvtosbt(aitv->it_value);
+                       callout_reset_sbt(&p->p_itcallout, sbt, pr,
+                           realitexpire, p, C_ABSOLUTE);
                }
                *oitv = p->p_realtimer;
                p->p_realtimer = *aitv;
@@ -793,7 +798,8 @@ void
 realitexpire(void *arg)
 {
        struct proc *p;
-       struct timeval ctv, ntv;
+       struct timeval ctv;
+       sbintime_t isbt;
 
        p = (struct proc *)arg;
        kern_psignal(p, SIGALRM);
@@ -803,19 +809,17 @@ realitexpire(void *arg)
                        wakeup(&p->p_itcallout);
                return;
        }
-       for (;;) {
+       isbt = tvtosbt(p->p_realtimer.it_interval);
+       if (isbt >= sbt_timethreshold)
+               getmicrouptime(&ctv);
+       else
+               microuptime(&ctv);
+       do {
                timevaladd(&p->p_realtimer.it_value,
                    &p->p_realtimer.it_interval);
-               getmicrouptime(&ctv);
-               if (timevalcmp(&p->p_realtimer.it_value, &ctv, >)) {
-                       ntv = p->p_realtimer.it_value;
-                       timevalsub(&ntv, &ctv);
-                       callout_reset(&p->p_itcallout, tvtohz(&ntv) - 1,
-                           realitexpire, p);
-                       return;
-               }
-       }
-       /*NOTREACHED*/
+       } while (timevalcmp(&p->p_realtimer.it_value, &ctv, <=));
+       callout_reset_sbt(&p->p_itcallout, tvtosbt(p->p_realtimer.it_value),
+           isbt >> tc_precexp, realitexpire, p, C_ABSOLUTE);
 }
 
 /*
@@ -830,8 +834,9 @@ itimerfix(struct timeval *tv)
 
        if (tv->tv_sec < 0 || tv->tv_usec < 0 || tv->tv_usec >= 1000000)
                return (EINVAL);
-       if (tv->tv_sec == 0 && tv->tv_usec != 0 && tv->tv_usec < tick)
-               tv->tv_usec = tick;
+       if (tv->tv_sec == 0 && tv->tv_usec != 0 &&
+           tv->tv_usec < (u_int)tick / 16)
+               tv->tv_usec = (u_int)tick / 16;
        return (0);
 }
 
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to