Hi,

We update the ITIMER_VIRTUAL and ITIMER_PROF per-process interval
timers from hardclock(9).  If a timer is enabled we call itimerdecr()
to update and reload it as needed.  If a timer has expired we then set
a flag on the current thread to signal itself when returning to
userspace.

However, there is a race here with setitimer(2).  In hardclock(9) we
check whether a timer is enabled *before* entering itimer_mtx in
itimerdecr(), but once we have entered the mutex we don't double-check
that the timer is still enabled.  This is wrong.  Another thread may
have disabled the timer via setitimer(2) while we were entering the
mutex.

This patch adds the second check to itimerdecr().  If we lost the race
and the timer is disabled we return 1 to indicate that the timer has
not expired, i.e. that the thread should take no action.

ok?

Index: kern_time.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_time.c,v
retrieving revision 1.134
diff -u -p -r1.134 kern_time.c
--- kern_time.c 8 Aug 2020 01:01:26 -0000       1.134
+++ kern_time.c 9 Aug 2020 11:47:02 -0000
@@ -682,6 +682,20 @@ itimerdecr(struct itimerspec *itp, long 
        NSEC_TO_TIMESPEC(nsec, &decrement);
 
        mtx_enter(&itimer_mtx);
+
+       /*
+        * Double-check that the timer is enabled.  We may have lost
+        * a race with another thread in setitimer(2) when entering
+        * itimer_mtx.
+        */
+       if (!timespecisset(&itp->it_value)) {
+               mtx_leave(&itimer_mtx);
+               return (1);
+       }
+
+       /*
+        * The timer is enabled.  Update and reload it as needed.
+        */
        timespecsub(&itp->it_value, &decrement, &itp->it_value);
        if (itp->it_value.tv_sec >= 0 && timespecisset(&itp->it_value)) {
                mtx_leave(&itimer_mtx);

Reply via email to