DF-2570 / trigger.c
/* * DF-2570 trigger PoC - ng_device_rcvdata kmalloc-then-mtod-then-kfree(M_DEVBUF) * * sys/netgraph/ng_device.c ng_device_rcvdata (lines 334-380) does: * * 363: buffer = kmalloc(sizeof(char)*m->m_len, M_DEVBUF, M_NOWAIT|M_ZERO); * 364-367: (NULL check) * 369: buffer = mtod(m, char *); <-- LEAKS the kmalloc'd buffer * and now points at m->m_data * (which lives in the MBUF zone) * 372: memcpy(connection->readq+connection->loc, buffer, m->m_len); * 377: kfree(buffer, M_DEVBUF); <-- FREES m->m_data through the * WRONG zone (M_DEVBUF), corrupting * the M_DEVBUF slab freelist AND * leaving the mbuf double-freeable * (freed again when the mbuf is * released through its own zone) * * Two distinct defects: * (1) memory leak - the kmalloc'd buffer is never freed (pointer overwritten) * (2) heap corrupt - kfree(M_DEVBUF) on a pointer that belongs to the mbuf * zone -> slab corruption + double-freeable mbuf * * TRIGGER PATH: ng_device_rcvdata is the netgraph data-receive callback. It * fires when DATA is sent TO an ng_device node's hook (NOT via read()/write() * on /dev/ngdN). A realistic trigger (if the module were live) would be: * * ngctl mkpeer ng_device ng_socket0 dummy ngd0 # create ng_device node * ngctl msg ng_device0: setdum ... # or send a packet * # ... or attach ng_eiface/ng_ether and push a frame onto the hook * * Sending any mbuf down the hook -> ng_device_rcvdata() -> the kmalloc/mtod/ * kfree(M_DEVBUF) corruption. No bounds on m->m_len required for the * wrong-zone free; a single byte suffices. * * PRIVILEGE NOTE: /dev/ngdN is created by make_dev() mode 0600 (uid 0 gid 0), * so creating/configuring the netgraph graph that feeds rcvdata requires root. * * DEAD-CODE NOTE: the cited file sys/netgraph/ng_device.c is orphaned: * - no entry in sys/conf/files (only netgraph7/ng_device.c at conf/files:1699) * - not in sys/config/X86_64_GENERIC * - nm /boot/kernel/kernel.debug | grep -c ng_device == 0 * - no ng_device.ko module on disk; the orphaned source fails to compile * against the current kernel headers (removed cdevsw/make_dev API). * See VERDICT.md and module_build_failure.txt. This PoC therefore only * documents the trigger path; it cannot exercise live code on this kernel. */ #include <sys/types.h> #include <stdio.h> int main(void) { printf("DF-2570: ng_device_rcvdata kmalloc/mtod/kfree(M_DEVBUF) heap corruption\n"); printf("Trigger path: send mbuf data to an ng_device node's hook.\n"); printf("REQUIRES: ng_device module built + loaded (NOT the case on default kernel).\n"); printf("\n"); printf("On this guest:\n"); printf(" - nm /boot/kernel/kernel.debug | grep -c ng_device = 0 (not in kernel)\n"); printf(" - sys/conf/files has NO entry for sys/netgraph/ng_device.c\n"); printf(" - the orphaned source does not compile (removed cdevsw API)\n"); printf(" - the maintained sys/netgraph7/ng_device.c has NO analogous bug\n"); printf("\nConclusion: dead code -> NOT REPRODUCED on this kernel.\n"); return 0; } |