⬢ DragonFlyBSD Kernel Audit
DF-3843 / map_sync_overflow_proof.c
← back to finding ↓ download raw
/*
 * DF-3843 arithmetic proof (userland, standalone).
 *
 * Reproduces the exact struct layout math of the mrsas RAID-map sync path
 * using the definitions copied verbatim from sys/dev/raid/mrsas/mrsas.h,
 * and shows that:
 *
 *  1. the destination buffer allocated in mrsas_setup_raidmap()
 *     (mrsas.c:1645) is  sizeof(MR_FW_RAID_MAP)
 *                      + sizeof(MR_LD_SPAN_MAP)*(MAX_LOGICAL_DRIVES-1) bytes,
 *  2. the write loop in mrsas_sync_map_info() (mrsas.c:3220-3224) writes
 *     sizeof(MR_LD_TARGET_SYNC) * ldCount bytes at offset 0 of that buffer,
 *     where ldCount is the FULL 32-bit firmware field (mrsas.c:3207),
 *  3. MR_ValidateMapInfo() (mrsas_fp.c:257-265) only checks that
 *     totalSize == (sizeof(MR_FW_RAID_MAP) - sizeof(MR_LD_SPAN_MAP)
 *                  + sizeof(MR_LD_SPAN_MAP)*ldCount)  (u32 arithmetic)
 *     -- a firmware that supplies a matching (totalSize, ldCount) pair for
 *     ANY ldCount passes validation; nothing bounds ldCount <= 64.
 *
 * Expected output shows a concrete (totalSize, ldCount=65536) pair that
 * passes validation and overwrites ~90 KB past the 172,200-byte map buffer.
 */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>

/* ---- verbatim from sys/dev/raid/mrsas/mrsas.h ---- */
#define MAX_SPAN_DEPTH 8
#define MAX_QUAD_DEPTH MAX_SPAN_DEPTH
#define MAX_RAIDMAP_SPAN_DEPTH (MAX_SPAN_DEPTH)
#define MAX_ROW_SIZE 32
#define MAX_RAIDMAP_ROW_SIZE (MAX_ROW_SIZE)
#define MAX_LOGICAL_DRIVES 64
#define MAX_RAIDMAP_LOGICAL_DRIVES (MAX_LOGICAL_DRIVES)
#define MAX_RAIDMAP_VIEWS (MAX_LOGICAL_DRIVES)
#define MAX_ARRAYS 128
#define MAX_RAIDMAP_ARRAYS (MAX_ARRAYS)
#define MAX_PHYSICAL_DEVICES 256
#define MAX_RAIDMAP_PHYSICAL_DEVICES (MAX_PHYSICAL_DEVICES)

typedef struct _MR_DEV_HANDLE_INFO {
    u_int16_t  curDevHdl;
    u_int8_t   validHandles;
    u_int8_t   reserved;
    u_int16_t  devHandle[2];
} MR_DEV_HANDLE_INFO;

typedef struct _MR_ARRAY_INFO {
    u_int16_t      pd[MAX_RAIDMAP_ROW_SIZE];
} MR_ARRAY_INFO;

typedef struct _MR_QUAD_ELEMENT {
    u_int64_t     logStart;
    u_int64_t     logEnd;
    u_int64_t     offsetInSpan;
    u_int32_t     diff;
    u_int32_t     reserved1;
} MR_QUAD_ELEMENT;

typedef struct _MR_SPAN_INFO {
    u_int32_t             noElements;
    u_int32_t             reserved1;
    MR_QUAD_ELEMENT quad[MAX_RAIDMAP_SPAN_DEPTH];
} MR_SPAN_INFO;

typedef struct _MR_LD_SPAN_ {
    u_int64_t      startBlk;
    u_int64_t      numBlks;
    u_int16_t      arrayRef;
    u_int8_t       spanRowSize;
    u_int8_t       spanRowDataSize;
    u_int8_t       reserved[4];
} MR_LD_SPAN;

typedef struct _MR_SPAN_BLOCK_INFO {
    u_int64_t          num_rows;
    MR_LD_SPAN   span;
    MR_SPAN_INFO block_span_info;
} MR_SPAN_BLOCK_INFO;

typedef struct _MR_LD_RAID {
    struct {
        u_int32_t     fpCapable           :1;
        u_int32_t     reserved5           :3;
        u_int32_t     ldPiMode            :4;
        u_int32_t     pdPiMode            :4;
        u_int32_t     encryptionType      :8;
        u_int32_t     fpWriteCapable      :1;
        u_int32_t     fpReadCapable       :1;
        u_int32_t     fpWriteAcrossStripe :1;
        u_int32_t     fpReadAcrossStripe  :1;
        u_int32_t     fpNonRWCapable      :1;
        u_int32_t     reserved4           :7;
    } capability;
    u_int32_t     reserved6;
    u_int64_t     size;

    u_int8_t      spanDepth;
    u_int8_t      level;
    u_int8_t      stripeShift;
    u_int8_t      rowSize;

    u_int8_t      rowDataSize;
    u_int8_t      writeMode;
    u_int8_t      PRL;
    u_int8_t      SRL;

    u_int16_t     targetId;
    u_int8_t      ldState;
    u_int8_t      regTypeReqOnWrite;
    u_int8_t      modFactor;
    u_int8_t      regTypeReqOnRead;
    u_int16_t     seqNum;

    struct {
        u_int32_t ldSyncRequired:1;
        u_int32_t regTypeReqOnReadLsValid:1;
        u_int32_t reserved:30;
    } flags;

    u_int8_t      LUN[8];
    u_int8_t      fpIoTimeoutForLd;
    u_int8_t      reserved2[3];
    u_int32_t     logicalBlockLength;
    struct {
        u_int32_t LdPiExp:4;
        u_int32_t LdLogicalBlockExp:4;
        u_int32_t reserved1:24;
    } exponent;
    u_int8_t      reserved3[0x80-0x38];
} MR_LD_RAID;

typedef struct _MR_LD_SPAN_MAP {
    MR_LD_RAID  ldRaid;
    u_int8_t    dataArmMap[MAX_RAIDMAP_ROW_SIZE];
    MR_SPAN_BLOCK_INFO  spanBlock[MAX_RAIDMAP_SPAN_DEPTH];
} MR_LD_SPAN_MAP;

typedef struct _MR_FW_RAID_MAP {
    u_int32_t  totalSize;
    union {
        struct {
            u_int32_t         maxLd;
            u_int32_t         maxSpanDepth;
            u_int32_t         maxRowSize;
            u_int32_t         maxPdCount;
            u_int32_t         maxArrays;
        } validationInfo;
        u_int32_t             version[5];
        u_int32_t             reserved1[5];
    } raid_desc;
    u_int32_t         ldCount;
    u_int32_t         Reserved1;
    u_int8_t          ldTgtIdToLd[MAX_RAIDMAP_LOGICAL_DRIVES+MAX_RAIDMAP_VIEWS];
    u_int8_t          fpPdIoTimeoutSec;
    u_int8_t           reserved2[7];
    MR_ARRAY_INFO      arMapInfo[MAX_RAIDMAP_ARRAYS];
    MR_DEV_HANDLE_INFO devHndlInfo[MAX_RAIDMAP_PHYSICAL_DEVICES];
    MR_LD_SPAN_MAP     ldSpanMap[1];
} MR_FW_RAID_MAP;

typedef struct _MR_FW_RAID_MAP_ALL {
    MR_FW_RAID_MAP raidMap;
    MR_LD_SPAN_MAP ldSpanMap[MAX_LOGICAL_DRIVES - 1];
} MR_FW_RAID_MAP_ALL;

typedef struct _MR_LD_TARGET_SYNC {
    u_int8_t  targetId;
    u_int8_t  reserved;
    u_int16_t seqNum;
} MR_LD_TARGET_SYNC;
/* ---- end verbatim ---- */

int
main(void)
{
    const uint32_t map_sz = (uint32_t)(sizeof(MR_FW_RAID_MAP) +
                            sizeof(MR_LD_SPAN_MAP) * (MAX_LOGICAL_DRIVES - 1));
    const uint32_t entry = (uint32_t)sizeof(MR_LD_TARGET_SYNC);
    const uint32_t capacity = map_sz / entry;
    uint32_t ldCount = 65536;
    uint32_t totalSize;

    printf("sizeof(MR_FW_RAID_MAP)      = %u (header comment says 0x3288=%u)\n",
        (unsigned)sizeof(MR_FW_RAID_MAP), 0x3288u);
    printf("sizeof(MR_LD_SPAN_MAP)      = %u (header comment says 0x9E0=%u)\n",
        (unsigned)sizeof(MR_LD_SPAN_MAP), 0x9E0u);
    printf("sizeof(MR_FW_RAID_MAP_ALL)  = %u\n", (unsigned)sizeof(MR_FW_RAID_MAP_ALL));
    printf("sizeof(MR_LD_TARGET_SYNC)   = %u\n", entry);
    printf("map_sz (mrsas.c:1645)       = %u  <- bus_dmamem_alloc size\n", map_sz);
    printf("capacity in sync entries    = %u  (max ldCount before OOB)\n", capacity);

    /* MR_ValidateMapInfo check, mrsas_fp.c:257-260 */
    totalSize = (uint32_t)(sizeof(MR_FW_RAID_MAP) - sizeof(MR_LD_SPAN_MAP) +
                  ((size_t)sizeof(MR_LD_SPAN_MAP) * ldCount));
    printf("\nFirmware supplies ldCount=%u, totalSize=0x%x:\n", ldCount, totalSize);
    printf("  MR_ValidateMapInfo: totalSize(0x%x) == expected(0x%x) -> PASSES\n",
        totalSize, totalSize);

    printf("  mrsas_sync_map_info loop writes %u * %u = %u bytes "
           "into a %u-byte buffer\n", ldCount, entry, ldCount * entry, map_sz);
    printf("  => %d bytes written PAST the end of the DMA allocation\n",
        (int)((uint64_t)ldCount * entry - map_sz));
    printf("  => %u entries land fully out of bounds, each entry =\n",
        ldCount - capacity);
    printf("     { targetId: FW u8, reserved: 0x00, seqNum: FW u16 }\n");

    /* the read side: MR_GetLDTgtId/MR_LdRaidGet index ldSpanMap[i], i < ldCount */
    printf("\nread side: MR_LdRaidGet(i)/MR_GetLDTgtId(i) read ldSpanMap[i] for\n"
           "  i up to %u; buffer holds %d span maps -> OOB read too\n",
        ldCount - 1, (int)(sizeof(MR_FW_RAID_MAP_ALL) / sizeof(MR_LD_SPAN_MAP)));

    if ((uint64_t)ldCount * entry <= map_sz) {
        printf("\nPROOF FAILED: no overflow\n");
        return (1);
    }
    printf("\nPROOF OK: loop length is firmware-controlled and unbounded; "
           "validation does not bound ldCount\n");
    return (0);
}