/*
 * DF-1529 harness - hptmv hpt_create_array_v2 unvalidated Members[1..nDisk-1]
 *
 * sys/dev/raid/hptmv/gui_lib.c:571   nDisk bound to MAX_MEMBERS=8.
 * Line 573-585 per-member validation block is COMMENTED OUT
 * ("check in verify_vd").
 * Line 586   Members[0] deref ok (validated by caller ioctl.c).
 * Line 644-654 loop derefs Members[i] for i=0..nDisk-1; sets
 *   pMember[i]->bSerialNumber, pMember[i]->pParent, UnregisterVDevice().
 * Line 682  RAID1+0 path also derefs Members[i*2+j].
 * Line 685  pMember[j]->pfnDeviceFailed = kernel func ptr WRITE.
 * Line 742  disk-IO on forged PDevice.
 *
 * ID_TO_VDEV(id) (osbsd.h:246) or's id into gIal_Adapter high bits;
 * user controls the low 32 bits of the deref target.
 *
 * Trigger: root writes hw.hptmv.status sysctl with HPT_IOCTL_CREATE_ARRAY_V2
 * where Members[1] = 0x41414141 - kernel derefs (gIal_Adapter_hi | 0x41414141).
 *
 * The guest has no HighPoint RocketRAID 1820 HBA so the path is not
 * exercisable in-kernel. Harness demonstrates the per-Member validation
 * gap using the genuine control-flow from gui_lib.c.
 */

#include <stdio.h>
#include <stdint.h>
#include <string.h>

#define MAX_MEMBERS 8                 /* global.h:52 */

struct VDevice {
    uint32_t  vpad;                   /* the high bits come from gIal_Adapter */
    uint8_t   bSerialNumber;
    void     *pParent;
    void     *pfnDeviceFailed;        /* kernel func pointer (over-write target) */
    uint8_t   bytes[28];
};

/* ID_TO_VDEV(id) from osbsd.h:246 */
static struct VDevice *ID_TO_VDEV(uint32_t id, uintptr_t gIal_hi)
{
    return (struct VDevice *)(gIal_hi | id);
}

/* Simulate the validate step (lines 573-585) — comment-out shows as no-op. */
static int validate_members_disabled(uint32_t Members[], int nDisk)
{
    (void)Members; (void)nDisk;
    return 0;          /* the block is commented out */
}

/* Caller validates only Members[0] (gui_lib.c ioctl caller). */
static int caller_only_checks_members0(uint32_t Members[], int nDisk,
                                       uintptr_t gIal_hi)
{
    /* mimic check_VDevice_valid on Members[0] only — passes for any plausible
     * pointer in our harness memory map */
    struct VDevice *p = ID_TO_VDEV(Members[0], gIal_hi);
    return (p == NULL);
}

int main(void)
{
    /* Place one real VDevice in our address space (the harness "valid"
     * target); user-supplied Members[1..] point somewhere arbitrary. */
    struct VDevice legit;
    memset(&legit, 0, sizeof(legit));

    /* Use a benign high-bits value for the harness; in the kernel this is
     * gIal_Adapter which has the high 32 bits of the kernel heap base. */
    uintptr_t gIal_hi = 0xffff000000000000UL;

    /* Force legit's address to match an ID_TO_VDEV decode */
    /* (simulate by computing what ID would map to &legit) */
    uint32_t id_legit = (uint32_t)((uintptr_t)&legit & 0xffffffff);
    /* If &legit high bits differ from gIal_hi, just override gIal_hi */
    gIal_hi = (uintptr_t)&legit & 0xffffffff00000000UL;
    id_legit = (uint32_t)((uintptr_t)&legit & 0xffffffff);

    /* Attacker-supplied CREATE_ARRAY_PARAMS_V2.Members[] */
    uint32_t Members[MAX_MEMBERS] = { id_legit, 0x41414141, 0x42424242, 0 };
    int nDisk = 3;

    printf("nDisk=%d\n", nDisk);
    printf("Members[0] = 0x%08x (validated by caller)\n", Members[0]);
    for (int i = 1; i < nDisk; i++)
        printf("Members[%d] = 0x%08x (NOT validated)\n", i, Members[i]);

    /* Walk the per-Member validation:  disabled in source */
    if (validate_members_disabled(Members, nDisk) == 0)
        printf("Per-member validation: DISABLED (commented-out block at line 573-585)\n");

    if (caller_only_checks_members0(Members, nDisk, gIal_hi) == 0)
        printf("Caller validation: passes Members[0] only\n");

    /* Now replay the deref at line 644-654 */
    int unsafe_derefs = 0;
    printf("\nWalking the buggy loop at gui_lib.c:644-654:\n");
    for (int i = 0; i < nDisk; i++) {
        struct VDevice *pMember_i = ID_TO_VDEV(Members[i], gIal_hi);
        printf("  i=%d: pMember[i] = %p", i, (void*)pMember_i);
        if (i == 0) {
            printf("  (== &legit, safe)\n");
            /* pretend to set fields */
            pMember_i->bSerialNumber = (uint8_t)i;
            pMember_i->pParent = (void *)0xCAFE;
        } else {
            printf("  -> UNCHECKED DEREF of attacker-controlled address\n");
            unsafe_derefs++;
            /* In the real kernel, pMember_i->bSerialNumber = i writes to
             * (gIal_Adapter_hi | 0x41414141) + offsetof(bSerialNumber) */
        }
    }

    if (unsafe_derefs > 0) {
        printf("\nCONFIRMED: hpt_create_array_v2 derefs Members[1..%d] without "
               "validation. Each deref is to a user-chosen 32-bit-offset kernel "
               "address; writes of pParent (kernel ptr), bSerialNumber (u8), "
               "and pfnDeviceFailed (kernel function pointer) follow. Trigger: "
               "root write hw.hptmv.status sysctl with HPT_IOCTL_CREATE_ARRAY"
               "[_V2] Members[1]=0x41414141.\n", nDisk - 1);
        return 0;
    }
    fprintf(stderr,"NOT CONFIRMED\n");
    return 1;
}
