/*
 * DF-1234 — ASR_queue_i (I2OUSRCMD) heap info-leak via u_int16_t truncation
 *
 * Bug (sys/dev/raid/asr/asr.c):
 *   Line 486:  ASR_fillMessage(void *Message, u_int16_t size)
 *     -> bzero(Message_Ptr, size) only bzeros the low 16 bits of size.
 *   Line 3262: ReplySizeInBytes = (I2O_MESSAGE_FRAME_getMessageSize(...) << 2)
 *     -> from user-controlled reply MessageSize field, up to 0xFFFF<<2 = ~256KB.
 *   Line 3273: kmalloc(ReplySizeInBytes, M_TEMP, M_WAITOK) -- NO M_ZERO.
 *   Line 3282: ASR_fillMessage(Reply_Ptr, ReplySizeInBytes)
 *     -> silently truncated to u_int16_t.
 *   Line 3575: copyout(Reply_Ptr, Reply, ReplySizeInBytes)
 *     -> ships up to 256KB of UNINITIALIZED slab to userspace.
 *
 * Example: a reply MessageSize of 0x4000 yields ReplySizeInBytes = 0x10000.
 * ASR_fillMessage's u_int16_t size param becomes 0 -> bzero does nothing.
 * Only ~30-60 bytes get touched by the header writes (setVersionOffset,
 * setMessageSize etc.). The remaining ~64KB of stale slab is copyout'd.
 *
 * Privilege: asr_open() at asr.c:3107 enforces SYSCAP_RESTRICTEDROOT -- only
 * root or a process granted the restricted-root capability can reach
 * I2OUSRCMD. So this is root->kernel info-leak (slab-grooming / KASLR-defeat
 * primitive) -- not unprivileged.
 *
 * THIS GUEST: no DPT SmartRAID controller in pciconf -l; /dev/asrN absent;
 * ioctl not reachable. PoC prints reachability. On a host with an asr(4)
 * controller, sending I2OUSRCMD with a crafted reply frame leaks up to 256KB
 * of kernel slab.
 */

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>

#ifndef I2OUSRCMD
#define I2OUSRCMD  _IOWR('A', 1, u_int8_t)  /* asr.c:3788 */
#endif

int
main(void)
{
    int fd, i;
    const char *devs[] = {"/dev/asr0", "/dev/asr1", "/dev/asr2", "/dev/asr3"};

    printf("[DF-1234] asr ASR_queue_i heap info-leak demonstrator\n");
    printf("[DF-1234] Bug: ASR_fillMessage takes u_int16_t size (asr.c:486);\n");
    printf("[DF-1234]      ReplySizeInBytes can be up to 0x3FFFC (~256KB).\n");
    printf("[DF-1234]      bzero truncates to 16 bits; kmalloc has no M_ZERO;\n");
    printf("[DF-1234]      copyout at :3575 ships uninitialized slab.\n\n");

    fd = -1;
    for (i = 0; i < 4; i++) {
        fd = open(devs[i], O_RDWR);
        if (fd >= 0) {
            printf("[DF-1234] Opened %s -- controller present.\n", devs[i]);
            printf("[DF-1234] (Triggering the actual leak requires SYSCAP_RESTRICTEDROOT,\n");
            printf("[DF-1234]  crafting a 0x4000-sized reply frame, and a controller.\n");
            printf("[DF-1234]  See VERDICT.md for the precise chain.)\n");
            close(fd);
            return (0);
        }
    }
    printf("[DF-1234] No /dev/asrN found: %s\n", strerror(errno));
    printf("[DF-1234] No DPT SmartRAID on guest -> ioctl not reachable.\n");
    printf("[DF-1234] Source-level verification only (see VERDICT.md).\n");
    return (0);
}
