DF-4621 / dp_link_logic_demo.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 122 123 124 125 | /* * dp_link_logic_demo.c -- DF-4621 supporting logic demonstration * * The vulnerable file (sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c) * is NOT COMPILED in DragonFly (absent from sys/dev/drm/amd/amdgpu/Makefile * SRCS lines 195-201) and its only callers are #if defined(CONFIG_DEBUG_FS) * (amdgpu_dm.c:478-479,4119-4120), so the kernel path cannot run on this * guest (also: no AMD GPU). This demo transcribes the *validation logic* * 1:1 (amdgpu_dm_debugfs.c:195-221) and the consumer loop 1:1 * (amdgpu_dm_debugfs.c:394-410) to show, at runtime, that: * * 1. input "5 a" (lane_count 5 = INVALID, link_rate 0xa = valid) passes * the single-flag `valid_input' check; * 2. the invalid lane_count is stored verbatim by * dc_link_set_preferred_link_settings (dc/core/dc.c:460-491 stores * without validation at :484); * 3. dp_phy_settings_write's apply loop (:403-410) then writes * lane_settings[4] -- past lane_settings[LANE_COUNT_DP_MAX = 4] * (include/link_service_types.h:71-75, dc/dc_dp_types.h:37). * * Transcription is asserted line-by-line in the comments; it is a logic * proof, NOT a verbatim compile (the kernel build is dead code). */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> /* enum dc_lane_count (dc/dc_dp_types.h:29-37) */ #define LANE_COUNT_UNKNOWN 0 #define LANE_COUNT_ONE 1 #define LANE_COUNT_TWO 2 #define LANE_COUNT_FOUR 4 #define LANE_COUNT_DP_MAX LANE_COUNT_FOUR /* link rates (subset, values from dc_dp_types.h) */ #define LINK_RATE_UNKNOWN 0 #define LINK_RATE_LOW 6 #define LINK_RATE_HIGH 10 /* 0xa */ struct dc_link_settings { int lane_count, link_rate, link_spread; }; struct dc_lane_settings { int VOLTAGE_SWING, PRE_EMPHASIS, POST_CURSOR2; }; struct link_training_settings { /* include/link_service_types.h:71-75 */ struct dc_link_settings link_settings; struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX]; bool allow_invalid_msa_timing_param; int canary; /* harness: detect writes past struct */ }; struct dc_link { struct dc_link_settings preferred_link_setting; int test_pattern_enabled; }; /* ---- transcribed from amdgpu_dm_debugfs.c:195-221 ---- */ static bool validate(long param0, long param1) { bool valid_input = false; switch (param0) { case LANE_COUNT_ONE: case LANE_COUNT_TWO: case LANE_COUNT_FOUR: valid_input = true; break; default: break; } switch (param1) { case LINK_RATE_LOW: case LINK_RATE_HIGH: valid_input = true; /* BUG: OR-combined, not AND */ break; default: break; } return valid_input; } int main(void) { long param[2] = {5, 0xa}; /* echo "5 a" > link_settings */ struct dc_link link = {{0, LINK_RATE_UNKNOWN, 0}, 1}; struct link_training_settings lts; struct dc_link_settings prefer; int r; printf("input: lane_count=5 (invalid: not 1/2/4), link_rate=0xa (valid)\n"); if (!validate(param[0], param[1])) { printf("rejected (fix behaves this way) -- no finding\n"); return 1; } printf("ACCEPTED by the single valid_input flag " "(amdgpu_dm_debugfs.c:195-221)\n"); /* dc.c:484 stores verbatim: */ prefer.lane_count = (int)param[0]; prefer.link_rate = (int)param[1]; prefer.link_spread = 0; link.preferred_link_setting = prefer; printf("stored preferred_link_setting.lane_count = %d " "(dc.c:484, unvalidated)\n", link.preferred_link_setting.lane_count); /* dp_phy_settings_write:386-400 (use_prefer path) + :403-410 loop */ memset(<s, 0, sizeof(lts)); lts.link_settings.lane_count = link.preferred_link_setting.lane_count; for (r = 0; r < lts.link_settings.lane_count; r++) { /* in the kernel: lane_settings[r] with r == 4 is OOB (array * of LANE_COUNT_DP_MAX = 4). Here r==4 writes past the * array into allow_invalid_msa_timing_param + canary. */ struct dc_lane_settings *p = <s.lane_settings[r]; p->VOLTAGE_SWING = 0; p->PRE_EMPHASIS = 1; p->POST_CURSOR2 = 2; if (r >= LANE_COUNT_DP_MAX) printf("loop iteration r=%d: OOB write at " "&lane_settings[%d] -- past " "lane_settings[LANE_COUNT_DP_MAX-1=%d]\n", r, r, LANE_COUNT_DP_MAX - 1); } printf("canary=%d allow_invalid_msa_timing_param overwritten=%d " "(stack OOB in kernel: adjacent frame data)\n", lts.canary, (int)lts.allow_invalid_msa_timing_param); printf("=> with lane_count 0xffffffff the loop is unbounded " "(linear stack smash)\n"); return 0; } |