summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkettenis <kettenis@openbsd.org>2015-04-28 20:54:18 +0000
committerkettenis <kettenis@openbsd.org>2015-04-28 20:54:18 +0000
commit05ffb7cef019f60b14cb1c941cb654f894700e2a (patch)
tree33e8c61026116667aecdc96f38a5748b8c4df21b
parentDon't grab the kernel lock for clock interrupts. The way we use mutexes (diff)
downloadwireguard-openbsd-05ffb7cef019f60b14cb1c941cb654f894700e2a.tar.xz
wireguard-openbsd-05ffb7cef019f60b14cb1c941cb654f894700e2a.zip
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@
-rw-r--r--sys/kern/kern_time.c23
1 files 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);
}