DragonFlyBSD Kernel Audit
DF-2570 / trigger.c
← back to finding ↓ download raw
/*
 * 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;
}