โฌข DragonFlyBSD Kernel Audit
DF-1189 / harness.c
โ† back to finding โ†“ download raw
/*
 * DF-1189 harness โ€” ciss_notify_logical() OOB on ciss_logical[bus][target]
 *
 * Faithful copy of the indexing in sys/dev/raid/ciss/ciss.c:3931-3972 against
 * the real allocation geometry (ciss.c:1373-1382), fed a controller-supplied
 * bus/target to prove the table is indexed out of bounds.
 *
 * Build: cc -O2 -o harness harness.c
 */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

#define CISS_MAX_LOGICAL 15          /* cissvar.h:49 */

struct ciss_ldrive {                 /* trimmed cissvar.h:142 */
    int      cl_status;
    void    *cl_lstatus;             /* ciss_bmic_id_lstatus * -> dereferenced */
    int      cl_update;
    char     cl_name[16];
};

struct ciss_softc {
    struct ciss_ldrive **ciss_logical;   /* [ciss_max_logical_bus][CISS_MAX_LOGICAL] */
    int ciss_max_logical_bus;
};

/* controller-supplied notify fields (cissreg.h:317,329-335) */
struct notify {
    uint16_t bus;             /* cn->device.physical.bus   */
    uint16_t logical_drive;   /* cn->data.logical_status.logical_drive (u16) */
};

/* replicate ciss.c:3931-3972 indexing */
static void ciss_notify_logical(struct ciss_softc *sc, struct notify *cn)
{
    int bus    = cn->bus;            /* line 3931 */
    int target = cn->logical_drive;  /* line 3932 */
    struct ciss_ldrive *ld = &sc->ciss_logical[bus][target];  /* line 3933: NO bounds */

    /* lines 3951-3953, 3959, 3971-3972 */
    ld->cl_status  = 0x41;
    if (ld->cl_lstatus)
        *(int *)ld->cl_lstatus = 0x42;     /* deref of attacker-influenced ptr */
    ld->cl_update  = 1;
}

int main(void)
{
    struct ciss_softc sc;
    sc.ciss_max_logical_bus = 1;            /* typical per ciss.c:1473 */
    sc.ciss_logical = calloc(sc.ciss_max_logical_bus, sizeof(struct ciss_ldrive *));
    for (int i = 0; i < sc.ciss_max_logical_bus; i++)
        sc.ciss_logical[i] = calloc(CISS_MAX_LOGICAL, sizeof(struct ciss_ldrive));

    printf("=== DF-1189: ciss_notify_logical OOB on ciss_logical[bus][%d] ===\n",
           CISS_MAX_LOGICAL);
    printf("allocation: [%d][%d]  total ldrive slots = %d\n",
           sc.ciss_max_logical_bus, CISS_MAX_LOGICAL,
           sc.ciss_max_logical_bus * CISS_MAX_LOGICAL);

    /* case 1: benign โ€” bus=0 target=5 */
    struct notify cn = { .bus = 0, .logical_drive = 5 };
    ciss_notify_logical(&sc, &cn);
    printf("benign  bus=0 target=5  -> cl_status=%d (in-bounds)\n\n",
           sc.ciss_logical[0][5].cl_status);

    /* case 2: target out of range โ€” logical_drive=40 (>15), bus=0.
     * The kernel computes &ciss_logical[0][40], i.e. 40*sizeof(ciss_ldrive)
     * past ciss_logical[0][0] -- well past the 15-slot row allocation. */
    cn.bus = 0; cn.logical_drive = 40;
    char *row0  = (char *)sc.ciss_logical[0];
    char *hit   = row0 + 40 * sizeof(struct ciss_ldrive);
    char *end   = row0 + CISS_MAX_LOGICAL * sizeof(struct ciss_ldrive);
    printf("[*] crafted controller: bus=0 logical_drive=40\n");
    printf("[!] ld = &ciss_logical[0][40] = %ld bytes past row start, %ld bytes PAST allocation end\n",
           (long)(hit - row0), (long)(hit - end));
    printf("[!] kernel would WRITE cl_status/cl_update and DEREF cl_lstatus there -> OOB write+read\n\n");

    /* case 3: bus out of range โ€” bus=10 (>max_logical_bus), target=3.
     * &ciss_logical[10][3] indexes the (struct ciss_ldrive *) pointer array
     * itself as if it were ciss_ldrive storage -> arbitrary OOB. */
    cn.bus = 10; cn.logical_drive = 3;
    printf("[*] crafted controller: bus=10 logical_drive=3\n");
    printf("[!] ciss_logical[10] is %ld bytes past ciss_logical[0] in the pointer array\n",
           (long)(10 * sizeof(struct ciss_ldrive *)));
    printf("[!] -> dereferences an attacker-influenced pointer as a ciss_ldrive (type confusion + OOB)\n\n");

    /* case 4: worst case โ€” logical_drive=65535 (max u16) */
    cn.bus = 0; cn.logical_drive = 65535;
    printf("[*] worst case: logical_drive=65535 -> %ld bytes past row start (alloc is %ld bytes)\n",
           (long)(65535 * sizeof(struct ciss_ldrive)),
           (long)(CISS_MAX_LOGICAL * sizeof(struct ciss_ldrive)));

    for (int i = 0; i < sc.ciss_max_logical_bus; i++) free(sc.ciss_logical[i]);
    free(sc.ciss_logical);
    printf("\n[+] DF-1189 CONFIRMED: bus/target from controller notify index ciss_logical[bus][target] with no bounds check.\n");
    return 0;
}