DragonFlyBSD Kernel Audit
DF-1754 / harness.c
← back to finding ↓ download raw
/*
 * DF-1754 - radeon_vce.c OOB read of p->relocs[] and wild pointer deref
 *           via non-multiple-of-4 chunk_relocs length_dw.
 *
 * Vulnerable code (sys/dev/drm/radeon/radeon_vce.c):
 *   470  int radeon_vce_cs_reloc(p, lo, hi, size) {
 *   482      if (idx >= relocs_chunk->length_dw) return -EINVAL;
 *   488      reloc = &p->relocs[(idx / 4)];
 *   489      start = reloc->gpu_offset;     // wild deref if OOB
 *   490      end   = start + radeon_bo_size(reloc->robj);
 *
 * nrelocs = chunk_relocs->length_dw / 4 (radeon_cs.c:89, integer division).
 * For length_dw not a multiple of 4 (e.g. 5): nrelocs=1, idx can be 4
 * (passes 4>=5 false), idx/4=1 indexes past the 1-element relocs array.
 * reloc->robj is then a wild pointer dereferenced at radeon_bo_size().
 *
 * Identical root cause to DF-1725 in radeon_cs.c (same nrelocs arithmetic).
 *
 * This harness is the same logic as DF-1725's harness, parameterized for
 * the radeon_vce path; see DF-1725/harness.c for the full arithmetic.
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

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

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

int main(void)
{
    unsigned length_dw = 5;
    unsigned nrelocs   = length_dw / 4;
    struct radeon_bo_list *relocs = calloc(nrelocs, sizeof(*relocs));
    relocs[0].robj = (void*)0xCAFE;

    printf("=== DF-1754 radeon_vce_cs_reloc idx/4 OOB harness ===\n");
    printf("chunk_relocs->length_dw = %u  (not multiple of 4)\n", length_dw);
    printf("p->nrelocs = %u\n", nrelocs);
    printf("\n");

    int oob = 0;
    for (unsigned idx = 0; idx < length_dw; idx++) {
        struct radeon_bo_list *r;
        if (vce_cs_reloc(length_dw, idx, &r, relocs, nrelocs) == 0) {
            unsigned slot = idx / 4;
            int is_oob = (slot >= nrelocs);
            printf("idx=%u -> relocs[%u]%s   reloc->robj=%p (wild deref at radeon_bo_size)\n",
                   idx, slot, is_oob ? "  <-- OOB" : "", r->robj);
            if (is_oob) oob++;
        }
    }
    printf("\n");
    if (oob > 0) {
        printf("VERDICT: BUG CONFIRMED. Same root cause as DF-1725:\n"
               "        chunk_relocs->length_dw=%u / 4 = nrelocs=%u, but the\n"
               "        (idx >= length_dw) check at line 482 admits idx=%u,\n"
               "        and relocs[%u] is OOB. Wild deref of reloc->robj.\n",
               length_dw, nrelocs, length_dw-1, (length_dw-1)/4);
        free(relocs);
        return 0;
    }
    printf("VERDICT: not reproduced.\n");
    free(relocs);
    return 1;
}