Skip to content

Commit

Permalink
Reduce minimal time intervals of setitimer(2) from 1/HZ to 1/(16*HZ) by
Browse files Browse the repository at this point in the history
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
  • Loading branch information
amotin committed Mar 6, 2013
1 parent 128087c commit 5dfd8a1
Showing 1 changed file with 25 additions and 20 deletions.
45 changes: 25 additions & 20 deletions sys/kern/kern_time.c
Expand Up @@ -691,7 +691,7 @@ kern_getitimer(struct thread *td, u_int which, struct itimerval *aitv)
*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
Expand Down Expand Up @@ -736,28 +736,33 @@ kern_setitimer(struct thread *td, u_int which, struct itimerval *aitv,
{
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;
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}

/*
Expand All @@ -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);
}

Expand Down

0 comments on commit 5dfd8a1

Please sign in to comment.