DF-5195 / deadlock_probe.c
/* * deadlock_probe.c -- DF-5195 clean reproduction (root, vidconsole boot) * * Parent: arms the snake saver, waits for it to draw, then forks a child * that issues CONS_SET (mode change) while the console is blanked. * The child is expected to HANG in the ioctl (kernel deadlock: * wait_scrn_saver_stop sleeps holding vga_token while the wake path * stop_scrn_saver->restore_scrn_saver_mode(TRUE)->set_mode needs it). * Parent kills the child after 10s, then demonstrates the permanent * aftermath: scrn_blanked stuck at 1, module unload EBUSY forever. */ #include <sys/ioctl.h> #include <sys/consio.h> #include <fcntl.h> #include <errno.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> static int rdcells(void) { unsigned char b[8192]; int fd = open("/dev/mem", O_RDONLY); int c = 0; if (fd < 0) return -1; pread(fd, b, 8192, 0xB8000); close(fd); for (int j = 0; j < 8192; j += 2) if (b[j+1] == 0x07 && b[j] != 0x20 && b[j]) c++; return c; } int main(void) { int fd, v = 1, mode = 32 /* M_VGA_C80x30 */, status, i; pid_t pid; fd = open("/dev/ttyv0", O_WRONLY); if (fd < 0) { perror("ttyv0"); return 1; } /* arm the saver: 2s blank time */ v = 2; if (ioctl(fd, CONS_BLANKTIME, &v)) { perror("BLANKTIME"); return 1; } printf("[1] waiting for snake to draw (VRAM cells)...\n"); for (i = 0; i < 50; i++) { usleep(200000); v = rdcells(); if (v > 5) break; } printf("[2] snake cells=%d (saver active, screen blanked)\n", v); if (v <= 5) { printf("[-] saver did not engage\n"); return 2; } pid = fork(); if (pid == 0) { int m = mode; printf("[3] child: CONS_SET(80x30) while blanked...\n"); fflush(stdout); errno = 0; v = ioctl(fd, CONS_SET, &m); printf("[?] child: CONS_SET returned rc=%d errno=%d " "(UNEXPECTED - should hang)\n", v, errno); _exit(0); } /* parent: give the child 10s to (not) finish */ for (i = 0; i < 100; i++) { usleep(100000); if (waitpid(pid, &status, WNOHANG) == pid) break; } if (i >= 100) { printf("[4] child still blocked after 10s in CONS_SET: " "DEADLOCK REPRODUCED (wchan 'scrsav')\n"); kill(pid, SIGKILL); waitpid(pid, &status, 0); printf("[5] child SIGKILLed (tsleep is PCATCH-killable)\n"); } else { printf("[4] child finished (no hang?!)\n"); } /* permanent aftermath */ sleep(2); printf("[6] post-kill: VRAM cells=%d; sleeping 10s to give the " "kernel every chance to recover...\n", rdcells()); sleep(10); printf("[7] after 12s: VRAM cells=%d; now 'kldunload snake_saver' " "(expect EBUSY/hang) and ps:\n", rdcells()); fflush(stdout); system("timeout 8 kldunload snake_saver; echo UNLOAD_RC=$?"); system("ps -o pid,stat,wchan,args -p $$ 2>/dev/null | head -2"); printf("[8] check kernel state with /tmp/kread (scrn_blanked)\n"); fflush(stdout); return 0; } |