From 05ffb7cef019f60b14cb1c941cb654f894700e2a Mon Sep 17 00:00:00 2001 From: kettenis Date: Tue, 28 Apr 2015 20:54:18 +0000 Subject: [PATCH] Protect the per-process itimerval structs with a mutex. We update these from hardclock() which runs without grabbing the kernel lock. This means that two threads could concurrently update the struct which could lead to corruption of the value which in turn could stop the timer. It could also result in getitimer(2) returning a non-normalized value. With help from guenther@. ok deraadt@, guenther@ --- sys/kern/kern_time.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/sys/kern/kern_time.c b/sys/kern/kern_time.c index a8808ed1b3b..453790b3682 100644 --- a/sys/kern/kern_time.c +++ b/sys/kern/kern_time.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_time.c,v 1.89 2014/12/07 02:58:14 deraadt Exp $ */ +/* $OpenBSD: kern_time.c,v 1.90 2015/04/28 20:54:18 kettenis Exp $ */ /* $NetBSD: kern_time.c,v 1.20 1996/02/18 11:57:06 fvdl Exp $ */ /* @@ -460,6 +460,8 @@ sys_adjtime(struct proc *p, void *v, register_t *retval) } +struct mutex itimer_mtx = MUTEX_INITIALIZER(IPL_CLOCK); + /* * Get value of an interval timer. The process virtual and * profiling virtual time timers are kept internally in the @@ -488,7 +490,6 @@ sys_getitimer(struct proc *p, void *v, register_t *retval) syscallarg(struct itimerval *) itv; } */ *uap = v; struct itimerval aitv; - int s; int which; which = SCARG(uap, which); @@ -496,11 +497,12 @@ sys_getitimer(struct proc *p, void *v, register_t *retval) if (which < ITIMER_REAL || which > ITIMER_PROF) return (EINVAL); memset(&aitv, 0, sizeof(aitv)); - s = splclock(); + mtx_enter(&itimer_mtx); aitv.it_interval.tv_sec = p->p_p->ps_timer[which].it_interval.tv_sec; aitv.it_interval.tv_usec = p->p_p->ps_timer[which].it_interval.tv_usec; aitv.it_value.tv_sec = p->p_p->ps_timer[which].it_value.tv_sec; aitv.it_value.tv_usec = p->p_p->ps_timer[which].it_value.tv_usec; + mtx_leave(&itimer_mtx); if (which == ITIMER_REAL) { struct timeval now; @@ -520,7 +522,7 @@ sys_getitimer(struct proc *p, void *v, register_t *retval) &aitv.it_value); } } - splx(s); + return (copyout(&aitv, SCARG(uap, itv), sizeof (struct itimerval))); } @@ -573,12 +575,10 @@ sys_setitimer(struct proc *p, void *v, register_t *retval) } pr->ps_timer[ITIMER_REAL] = aitv; } else { - int s; - itimerround(&aitv.it_interval); - s = splclock(); + mtx_enter(&itimer_mtx); pr->ps_timer[which] = aitv; - splx(s); + mtx_leave(&itimer_mtx); } return (0); @@ -676,7 +676,7 @@ itimerround(struct timeval *tv) int itimerdecr(struct itimerval *itp, int usec) { - + mtx_enter(&itimer_mtx); if (itp->it_value.tv_usec < usec) { if (itp->it_value.tv_sec == 0) { /* expired, and already in next interval */ @@ -688,8 +688,10 @@ itimerdecr(struct itimerval *itp, int usec) } itp->it_value.tv_usec -= usec; usec = 0; - if (timerisset(&itp->it_value)) + if (timerisset(&itp->it_value)) { + mtx_leave(&itimer_mtx); return (1); + } /* expired, exactly at end of interval */ expire: if (timerisset(&itp->it_interval)) { @@ -701,6 +703,7 @@ expire: } } else itp->it_value.tv_usec = 0; /* sec is already 0 */ + mtx_leave(&itimer_mtx); return (0); } -- 2.20.1