DragonFlyBSD Kernel Audit
DF-1725 / harness.c
← back to finding ↓ download raw
/*
 * DF-1725 - radeon_cs.c heap OOB read in radeon_cs_packet_next_reloc
 *           via non-4-aligned chunk_relocs length_dw.
 *
 * Vulnerable code:
 *   sys/dev/drm/radeon/radeon_cs.c:89    p->nrelocs = chunk->length_dw / 4;
 *   sys/dev/drm/radeon/radeon_cs.c:90    p->relocs = kvmalloc_array(nrelocs,...);
 *   sys/dev/drm/radeon/radeon_cs.c:865   if (idx >= relocs_chunk->length_dw) return -EINVAL;
 *   sys/dev/drm/radeon/radeon_cs.c:878   *cs_reloc = &p->relocs[(idx / 4)];
 *
 * If chunk_relocs->length_dw is not a multiple of 4 (e.g. 5), then
 * nrelocs=1 but idx can be 4 (passes the 4<5 check), idx/4 = 1, so
 * p->relocs[1] is one-past-end OOB.  Callers then dereference
 * reloc->robj/gpu_offset -> wild pointer / info leak.
 *
 * DF-1754 is the identical bug in radeon_vce.c:radeon_vce_cs_reloc.
 *
 * This harness simulates the index arithmetic and shows the OOB.
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

struct radeon_bo_list { uint64_t gpu_offset; uint32_t tiling_flags; void *robj; };

int next_reloc(unsigned length_dw, unsigned idx_in, struct radeon_bo_list **out,
               struct radeon_bo_list *relocs, unsigned nrelocs)
{
    unsigned idx = idx_in;
    /* radeon_cs.c:865 / radeon_vce.c:482 */
    if (idx >= length_dw) return -1;
    /* radeon_cs.c:878 / radeon_vce.c:488 */
    *out = &relocs[idx / 4];
    return 0;
}

int main(void)
{
    unsigned length_dw = 5;          /* non-multiple-of-4 -> nrelocs=1 */
    unsigned nrelocs   = length_dw / 4;
    struct radeon_bo_list *relocs = calloc(nrelocs, sizeof(*relocs));
    relocs[0].robj = (void *)0xDEADBEEF;

    printf("=== DF-1725 / DF-1754 radeon relocs idx/4 OOB harness ===\n");
    printf("chunk_relocs->length_dw = %u (not multiple of 4)\n", length_dw);
    printf("p->nrelocs = length_dw/4 = %u (allocated array size)\n", nrelocs);
    printf("\n");

    int oob = 0;
    for (unsigned idx = 0; idx < length_dw; idx++) {
        struct radeon_bo_list *r;
        int rc = next_reloc(length_dw, idx, &r, relocs, nrelocs);
        if (rc == 0) {
            unsigned slot = idx / 4;
            int is_oob = (slot >= nrelocs);
            printf("idx=%u passes check, indexes relocs[%u]%s  (robj=%p)\n",
                   idx, slot, is_oob ? "  <-- OOB" : "", r->robj);
            if (is_oob) oob++;
        }
    }

    printf("\n");
    if (oob > 0) {
        printf("VERDICT: BUG CONFIRMED. length_dw=%u yields nrelocs=%u but\n"
               "        idx=%u (valid per the >= length_dw check) maps to\n"
               "        relocs[%u] which is one-past-end OOB. Wild deref of\n"
               "        reloc->robj/gpu_offset follows. Same root cause as\n"
               "        DF-1754 in radeon_vce.c.\n",
               length_dw, nrelocs, length_dw-1, (length_dw-1)/4);
        free(relocs);
        return 0;
    }
    printf("VERDICT: not reproduced.\n");
    free(relocs);
    return 1;
}