DragonFlyBSD Kernel Audit
DF-2929 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/linux_hrtimer.c
+++ b/sys/dev/drm/linux_hrtimer.c
@@ -106,6 +106,30 @@
 	/* Prevent arbitrarily short timeouts from being scheduled. */
 	timer->timeout_us = max(500, timer->timeout_us);
 
+	/*
+	 * Linux hrtimer semantics: hrtimer_start() on an already-armed
+	 * timer dequeues it and re-arms.  We must not re-run
+	 * systimer_init_oneshot() on a queued systimer: it bzero()s the
+	 * struct, leaving stale links in the owning cpu's gd_systimerq
+	 * (ghost links => timer queue corruption, panics in
+	 * systimer_intr()/systimer_del() and wild unlinks on production
+	 * kernels).  Delete the pending systimer first, on its owning
+	 * cpu, exactly like hrtimer_cancel() does.
+	 *
+	 * The delete is a no-op if the timer already fired (it is no
+	 * longer queued); SYSTF_ONQUEUE is cleared by then.
+	 */
+	if (timer->active) {
+		if (timer->gd == mycpu) {
+			systimer_del(&timer->st);
+		} else {
+			struct globaldata *oldcpu = mycpu;
+			lwkt_setcpu_self(timer->gd);
+			systimer_del(&timer->st);
+			lwkt_setcpu_self(oldcpu);
+		}
+	}
+
 	lwkt_gettoken(&timer->timer_token);
 
 	timer->cancel = false;