DragonFlyBSD Kernel Audit
DF-1473 / harness.c
← back to finding ↓ download raw
/*
 * DF-1473 harness — mpr_sas_lsi.c u16 truncation in EventDataLength*4
 *
 * sys/dev/raid/mpr/mpr_sas_lsi.c:136   u16 sz;
 *                                :148  sz = le16toh(event->EventDataLength) * 4;
 *                                :149  fw_event->event_data = kmalloc(sz, ...);
 *                                :156  bcopy(event->EventData, fw_event->event_data, sz);
 *
 * EventDataLength is u16 (mpi2_ioc.h:563). The multiplication by 4 promotes
 * to int, but the assignment back to u16 truncates the high half:
 *   - EventDataLength = 0x4000  -> sz = 0x10000 -> truncate -> 0
 *       kmalloc(0) returns ZERO_LENGTH_PTR (-8), non-NULL, so the NULL check
 *       passes; later the taskqueue thread derefs fw_event->event_data
 *       (== -8) -> page fault panic
 *   - EventDataLength = 0x4001  -> sz = 0x10004 -> truncate -> 4
 *       kmalloc(4) succeeds; bcopy reads only 4 bytes from the event reply
 *       but later parsing walks the struct fields past 4 bytes -> OOB heap
 *       read of the M_MPR slab bucket
 *
 * The guest has no LSI SAS3 HBA, so this is a harness proof against the
 * genuine arithmetic from the cited source line.
 */

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

int main(void)
{
    int bad = 0;
    struct { const char *name; uint16_t edl; } cases[] = {
        { "normal EventDataLength=8",     8 },
        { "EventDataLength=24 (reply sz)", 24 },
        { "EventDataLength=0x4000 -> sz=0",  0x4000 },
        { "EventDataLength=0x4001 -> sz=4",  0x4001 },
        { "EventDataLength=0x4002 -> sz=8",  0x4002 },
        { "EventDataLength=0xFFFF -> sz=0xFFFC", 0xFFFF },
    };
    size_t n = sizeof(cases)/sizeof(cases[0]);
    printf("%-36s %10s %12s %12s\n",
           "case","EDL","buggy_sz","correct_sz");
    for (size_t i=0;i<n;i++) {
        uint16_t edl = cases[i].edl;
        /* buggy line 148 */
        uint16_t buggy_sz = (uint16_t)(edl * 4);
        /* correct computation */
        uint32_t correct_sz = (uint32_t)edl * 4;
        printf("%-36s %10u %12u %12u\n",
               cases[i].name, (unsigned)edl, (unsigned)buggy_sz, correct_sz);
        if (buggy_sz != correct_sz) bad++;
    }
    printf("\nBuggy: %d/%zu cases produce a truncated (wrong) size.\n", bad, n);
    if (bad > 0) {
        printf("CONFIRMED: u16 sz truncation at mpr_sas_lsi.c:148 yields "
               "kmalloc(0) -> ZERO_LENGTH_PTR deref panic (EDL=0x4000) and "
               "kmalloc(4)-then-OOB-parse (EDL=0x4001). A malicious or "
               "compromised SAS3 HBA programming the reply DMA can choose EDL "
               "to control the truncated size precisely.\n");
        return 0;
    }
    fprintf(stderr,"NOT CONFIRMED\n");
    return 1;
}