/*
 * DF-1837 source-confirmation harness (amdgpu_dm_helpers EDID extensions overflow).
 *
 * sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c:583-584:
 *    sink->dc_edid.length = EDID_LENGTH * (edid->extensions + 1);
 *    memmove(sink->dc_edid.raw_edid, (uint8_t*)edid, sink->dc_edid.length);
 * raw_edid is uint8_t[DC_MAX_EDID_BUFFER_SIZE=512] (dc_types.h:98,169).
 * edid->extensions is u8 (0..255); EDID_LENGTH=128.  So length can be up
 * to 128*256=32768, overflowing raw_edid by up to 32256 bytes into the
 * heap-allocated dc_sink (kzalloc dc_sink.c:87).  Sibling dc.c:1774 DOES
 * check (len > DC_MAX_EDID_BUFFER_SIZE) before its memmove at dc.c:1794.
 *
 * This needs AMD GPU HW + crafted EDID (HW-gated).  The harness reproduces
 * the multiplication overflow and the resulting memmove length.
 *
 * Build:  cc -O2 -o harness harness.c
 * Run:    ./harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define EDID_LENGTH 128                  /* drm_edid.h:32 */
#define DC_MAX_EDID_BUFFER_SIZE 512      /* dc_types.h:98 */

struct dc_edid { int length; unsigned char raw_edid[DC_MAX_EDID_BUFFER_SIZE]; };

int main(void) {
    /* Place dc_edid at the start of a region with a sentinel tail. */
    unsigned char *blob = calloc(1, sizeof(struct dc_edid) + 65536);
    struct dc_edid *edid_field = (struct dc_edid *)blob;
    unsigned char *sentinel = blob + sizeof(struct dc_edid);
    for (int i = 0; i < 65536; i++) sentinel[i] = 0xCD;

    /* Crafted EDID: extensions = 4 (the README's example).
     * In the kernel, drm_get_edid reads edid[0x7e] = extensions and only
     * validates per-block checksums (drm_edid.c:1649), no upper bound. */
    unsigned char edid_extensions = 4;
    unsigned int length = EDID_LENGTH * (edid_extensions + 1);   /* line 583 */

    printf("DF-1837: dm_helpers_read_local_edid (amdgpu_dm_helpers.c:583-584)\n");
    printf("  edid->extensions = %u\n", edid_extensions);
    printf("  computed length = EDID_LENGTH * (ext+1) = %u\n", length);
    printf("  raw_edid[DC_MAX_EDID_BUFFER_SIZE=%d] (dc_types.h:98)\n",
           DC_MAX_EDID_BUFFER_SIZE);
    printf("  OVERFLOW = %d bytes past raw_edid into dc_sink heap object\n",
           length - DC_MAX_EDID_BUFFER_SIZE);

    /* Model the memmove (clipped to the sentinel region so we can SEE it). */
    unsigned int to_copy = length;
    if (to_copy > sizeof(struct dc_edid) + 65536) to_copy = sizeof(struct dc_edid) + 65536;
    memset(blob, 0xAB, to_copy);

    int corrupted = 0;
    for (int i = 0; i < 65536; i++) if (sentinel[i] != 0xCD) corrupted++;
    printf("  Harness: simulated memmove wrote %d sentinel bytes past raw_edid\n",
           corrupted);

    /* Sibling dc.c:1774 DOES bound it. */
    printf("  Contrast dc.c:1774 which checks (len > DC_MAX_EDID_BUFFER_SIZE) "
           "before memmove — the bound is required and missing here.\n");
    free(blob);
    return 0;
}
