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
This commit is contained in:
Alexander Motin 2013-03-06 22:40:47 +00:00
parent 0e7919f704
commit b5ea3779da

View File

@ -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
@ -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;
@ -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);
}