⬢ DragonFlyBSD Kernel Audit
DF-5195 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/misc/syscons/syscons.c
+++ b/sys/dev/misc/syscons/syscons.c
@@ -3534,11 +3534,27 @@ sc_clean_up(scr_stat *scp, int need_unlock)
 {
 #if NSPLASH > 0
     int error;
 #endif /* NSPLASH */
 
     lwkt_gettoken(&vga_token);
     if (scp->sc->flags & SC_SCRN_BLANKED) {
 	sc_touch_scrn_saver();
 #if NSPLASH > 0
+	/*
+	 * DF-5195: we may be called with vga_token held by our caller
+	 * (scioctl, sc_set_text_mode, the KDSETMODE/VT_ACTIVATE branches).
+	 * The wake path of wait_scrn_saver_stop() -- scrn_timer ->
+	 * stop_scrn_saver() -> restore_scrn_saver_mode(TRUE) -> set_mode() --
+	 * must itself lwkt_gettoken(&vga_token) (see set_mode()), so sleeping
+	 * here with the token held deadlocks the console subsystem.  Drop OUR
+	 * reference (acquired just above; tokens are released in strict LIFO
+	 * order and ours is the innermost) around the sleep and re-acquire it
+	 * so every caller keeps its held-on-return invariant.
+	 */
+	lwkt_reltoken(&vga_token);
 	if ((error = wait_scrn_saver_stop(scp->sc, need_unlock))) {
+	    lwkt_gettoken(&vga_token);
 	    lwkt_reltoken(&vga_token);
 	    return error;
 	}
+	lwkt_gettoken(&vga_token);
 #endif /* NSPLASH */
     }
     scp->status |= MOUSE_HIDDEN;