DF-5437 / hx5437_stall.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | /* * DF-5437 KLD harness -- ulpt(4) open() can be stalled uninterruptibly for * ~2x the SOFT_RESET control-request timeout by an evil printer that NAKs * the data/status stage. * * Production chain modelled (constants and shapes verbatim): * * open("/dev/ulpt0", O_RDONLY) (world-openable, 0644) * -> ulpt_open() ulpt.c:406-418: sc_fflags==0 -> ulpt_reset(sc) * -> ulpt_reset() ulpt.c:177-187: TWO sequential * usbd_do_request_flags(.., NULL, 0, NULL, 2 * USB_MS_HZ) * (2 s timeout each -- ulpt.c:180,183) * -> usbd_do_request_flags() usb_request.c:402-708: * xfer->timeout = timeout (usb_request.c:557) * usbd_transfer_start(xfer) * while (usbd_transfer_pending(xfer)) { * lksleep(xfer, xfer->xroot->xfer_lock, 0, * "WXFER", hz); (:648-650) * } * The lksleep() has NO PCATCH -> a signal (incl. SIGKILL) cannot * break it; only the xfer timeout (2 s per request) terminates the * wait. An evil device NAKing the control data stage stretches * every open() to ~2x2 s of uninterruptible sleep; a loop of opens * from an unprivileged user pins n kernel threads. * * Harness: /dev/hx5437ctl (0666). Its d_open() runs the production chain * shape: two sequential "requests", each completed by a callout after * hw.hx5437.timeout_ms (default 2000 == 2*USB_MS_HZ), with the production * wait loop verbatim (lksleep flags=0, wmesg "WXFER", timo hz). * * fix.diff mechanism: bound the per-request timeout (USB_MS_HZ/4 = 250 ms) * -> sysctl hw.hx5437.timeout_ms=250 mirrors the patched driver. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/lock.h> #include <sys/conf.h> #include <sys/device.h> #include <sys/callout.h> #include <sys/sysctl.h> #include <sys/fcntl.h> #include <sys/time.h> static struct lock hx_lk; /* xfer->xroot->xfer_lock stand-in */ static struct callout hx_co; /* ctrl xfer timeout stand-in */ static volatile int hx_pending; /* usbd_transfer_pending() stand-in */ static int hx_timeout_ms = 2000; /* 2 * USB_MS_HZ, ulpt.c:180 */ SYSCTL_NODE(_hw, OID_AUTO, hx5437, CTLFLAG_RW, 0, "DF-5437 harness"); SYSCTL_INT(_hw_hx5437, OID_AUTO, timeout_ms, CTLFLAG_RW, &hx_timeout_ms, 0, "per-request ctrl timeout (production 2000 = 2*USB_MS_HZ; fix 250)"); static void hx_ctrl_timeout(void *arg __unused) { /* the control xfer's timeout callout fires: usbd_transfer_done() */ hx_pending = 0; wakeup(&hx_pending); } static int hx_do_request_production(void) { int ticks; /* usbd_do_request_flags() request path for one SOFT_RESET: * xfer->timeout = timeout (usb_request.c:557) * usbd_transfer_start(xfer) * while (usbd_transfer_pending(xfer)) * lksleep(xfer, xfer_lock, 0, "WXFER", hz); (:648-650) */ hx_pending = 1; ticks = (hx_timeout_ms * hz + 999) / 1000; if (ticks < 1) ticks = 1; callout_reset(&hx_co, ticks, hx_ctrl_timeout, NULL); lockmgr(&hx_lk, LK_EXCLUSIVE); while (hx_pending) { lksleep(&hx_pending, &hx_lk, 0, "WXFER", hz); } lockmgr(&hx_lk, LK_RELEASE); return (0); /* USB_ERR_TIMEOUT == give up */ } static d_open_t hx_open; static struct dev_ops hx_ops = { { "hx5437ctl", 0, D_MPSAFE }, /* production: usb_ops (D_MPSAFE) */ .d_open = hx_open, }; static cdev_t hx_dev; static int hx_open(struct dev_open_args *ap __unused) { struct timespec ts0, ts1; int err1, err2; getnanouptime(&ts0); /* ulpt_reset(): ulpt.c:179-183 -- try 1.0 spec, then 1.1 spec */ err1 = hx_do_request_production(); err2 = hx_do_request_production(); getnanouptime(&ts1); kprintf("HX5437: open() complete: ELAPSED=%ld ms " "(req1=%d req2=%d, timeout_ms=%d)\n", (ts1.tv_sec - ts0.tv_sec) * 1000 + (ts1.tv_nsec - ts0.tv_nsec) / 1000000, err1, err2, hx_timeout_ms); return (0); } static int hx5437_modevent(module_t mod, int type, void *arg __unused) { switch (type) { case MOD_LOAD: lockinit(&hx_lk, "hx5437lk", 0, 0); callout_init(&hx_co); hx_dev = make_dev(&hx_ops, 0, UID_ROOT, GID_WHEEL, 0666, "hx5437ctl"); kprintf("HX5437: loaded (timeout_ms=%d, production 2*USB_MS_HZ)" "\n", hx_timeout_ms); return (0); case MOD_UNLOAD: destroy_dev(hx_dev); callout_stop(&hx_co); lockuninit(&hx_lk); kprintf("HX5437: unloaded\n"); return (0); default: return (EOPNOTSUPP); } } static moduledata_t hx5437_mod = { "hx5437stall", hx5437_modevent, NULL }; DECLARE_MODULE(hx5437stall, hx5437_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); |