DF-1837 / harness.c
/* * 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; } |