/*
 * DF-1300 harness — dc_add_all_planes_for_stream stack overflow (dead API)
 *
 * Reproduces the vulnerable write pattern of
 *   sys/dev/drm/amd/display/dc/core/dc_resource.c:1557-1574
 * in userspace.
 *
 * The kernel function:
 *   bool dc_add_all_planes_for_stream(..., int plane_count, ...)
 *   {
 *       struct dc_validation_set set;          // on the stack
 *       ...
 *       set.plane_count = plane_count;         // raw caller, NO clamp
 *       for (i = 0; i < plane_count; i++)
 *           set.plane_states[i] = plane_states[i];   // NO check vs MAX_SURFACES
 *       ...
 *   }
 *
 * `struct dc_validation_set` (dc.h:597-601) is:
 *     struct dc_stream_state *stream;          // offset 0
 *     struct dc_plane_state *plane_states[MAX_SURFACES=3];  // offset 8, 3 ptrs
 *     uint8_t plane_count;                     // offset 32 (immediately after)
 *
 * So plane_states[3] aliases plane_count + padding; plane_states[4+] aliases
 * the saved frame pointer / return address on the stack. A plane_count > 3
 * is a classic stack buffer overflow.
 *
 * The function is a DEAD public API: zero in-tree callers (only the
 * definition at dc_resource.c:1557 and the prototype at dc_stream.h:218).
 * So the bug is a LATENT HAZARD: a future DC consumer that wires it without
 * clamping plane_count to MAX_SURFACES gets a stack overflow.
 *
 * Build:  cc -O2 -Wall -o harness harness.c
 * Run:    ./harness
 *
 * Proof strategy: replicate the struct layout with canary fields after
 * plane_states[], call the function with plane_count=8, and show that the
 * writes past plane_states[2] corrupt the adjacent canary (which stands in
 * for plane_count + saved registers in the real frame).
 */

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

#define MAX_SURFACES 3   /* dc.h:43 */

struct dc_stream_state { int dummy; };
struct dc_plane_state  { int dummy; };

/* Faithful replica of dc_validation_set (dc.h:597-601), plus trailing canary
 * fields that stand in for the stack frame past the struct (plane_count,
 * padding, saved RBP, return address). */
struct dc_validation_set_with_canary {
    struct dc_stream_state *stream;                          /* offset 0  */
    struct dc_plane_state  *plane_states[MAX_SURFACES];     /* offset 8  (3 ptrs) */
    uint8_t plane_count;                                     /* offset 32 */
    uint8_t pad[7];                                          /* padding   */
    /* --- canary: represents stack frame past the struct --- */
    uint64_t saved_rbp_canary;
    uint64_t saved_rip_canary;
} __attribute__((packed));  /* pack to make offsets deterministic */

#define CANARY_RBP 0xDEADBEEFCAFEBABEULL
#define CANARY_RIP 0x1122334455667788ULL

/* Faithful replica of dc_add_all_planes_for_stream (dc_resource.c:1564-1571),
 * operating on a caller-provided frame so we can inspect the overflow.
 *
 * We index `plane_states` through a flat `void **` cast so the compiler
 * cannot see the [MAX_SURFACES] array bound and (incorrectly, at -O2) elide
 * the OOB iterations as UB. This faithfully represents the LOGICAL bug: there
 * is NO `if (plane_count > MAX_SURFACES) return false;` check. NOTE: in the
 * real kernel, gcc -O2 with -faggressive-loop-optimizations MIGHT silently
 * clamp the loop to MAX_SURFACES iterations because the array bound is
 * visible — but that is fragile (depends on gcc version/flags) and the
 * correct fix is the explicit check, not relying on optimizer UB-elision. */
static int vulnerable_add_planes(struct dc_validation_set_with_canary *set,
                                 struct dc_plane_state * const *plane_states,
                                 int plane_count)
{
    int i;
    void **flat = (void **)set->plane_states;   /* flat view, no [3] bound */
    set->plane_count = (uint8_t)plane_count;     /* :1568 raw, NO clamp */
    for (i = 0; i < plane_count; i++)            /* :1570 NO check vs MAX_SURFACES */
        flat[i] = plane_states[i];               /* :1571 the overflow */
    return 0;
}

int main(void)
{
    printf("DF-1300 dc_add_all_planes_for_stream stack-overflow harness (dead API)\n");
    printf("MAX_SURFACES=%d  sizeof(plane_states)=%zu (3 ptrs)\n",
           MAX_SURFACES, MAX_SURFACES * sizeof(struct dc_plane_state *));

    struct dc_validation_set_with_canary set;
    memset(&set, 0, sizeof(set));
    set.saved_rbp_canary = CANARY_RBP;
    set.saved_rip_canary = CANARY_RIP;

    /* Attacker-supplied plane array with plane_count = 8 (> MAX_SURFACES).
     * Each entry is a distinct recognizable pointer. */
    struct dc_plane_state planes[8];
    struct dc_plane_state *plane_ptrs[8];
    for (int i = 0; i < 8; i++) { planes[i].dummy = 0xA0 + i; plane_ptrs[i] = &planes[i]; }
    int plane_count = 8;

    printf("calling vulnerable_add_planes(plane_count=%d) -- MAX_SURFACES=%d\n",
           plane_count, MAX_SURFACES);
    int oob_writes = (plane_count > MAX_SURFACES) ? (plane_count - MAX_SURFACES) : 0;
    printf("=> %d OOB writes past plane_states[MAX_SURFACES-1] expected "
           "(each writes a pointer into the frame past plane_states[])\n", oob_writes);
    printf("BEFORE: plane_count=%u  saved_rbp_canary=0x%016llx  saved_rip_canary=0x%016llx\n",
           set.plane_count,
           (unsigned long long)set.saved_rbp_canary,
           (unsigned long long)set.saved_rip_canary);

    vulnerable_add_planes(&set, plane_ptrs, plane_count);

    int overflows = 0;
    printf("AFTER:\n");
    for (int i = 0; i < MAX_SURFACES; i++) {
        printf("  plane_states[%d] = %p  (in-bounds)\n",
               i, (void *)set.plane_states[i]);
    }
    /* plane_states[3] aliases plane_count + pad; plane_states[4+] aliases canaries. */
    printf("  plane_count (overwritten by [3]) = %u\n", set.plane_count);
    printf("  saved_rbp_canary = 0x%016llx  %s\n",
           (unsigned long long)set.saved_rbp_canary,
           (set.saved_rbp_canary != CANARY_RBP) ? "*** CORRUPTED ***" : "(intact)");
    printf("  saved_rip_canary = 0x%016llx  %s\n",
           (unsigned long long)set.saved_rip_canary,
           (set.saved_rip_canary != CANARY_RIP) ? "*** CORRUPTED ***" : "(intact)");

    if (set.saved_rbp_canary != CANARY_RBP || set.saved_rip_canary != CANARY_RIP)
        overflows = 1;
    if (overflows) {
        printf("RESULT: stack buffer overflow CONFIRMED at dc_resource.c:1570-1571 "
               "(plane_count>MAX_SURFACES overwrites frame past plane_states[])\n");
        printf("IN-KERNEL IMPACT: latent (dead API, 0 in-tree callers) — stack overflow / "
               "potential control-flow hijack if a future DC consumer wires it without clamping\n");
        return 0;
    }
    printf("UNEXPECTED: no overflow detected\n");
    return 1;
}
