DF-3653 / harness.c
/* * DF-3653 harness โ userspace model of the NULL-stream deref in * amdgpu_dm_atomic_commit_tail() -> update_stream_scaling_settings(). * * Kernel path (sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c): * 4861 for_each_oldnew_connector_in_state(...) * 4864 acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc); * 4873 if (!acrtc || drm_atomic_crtc_needs_modeset(new_crtc_state)) continue; * 4877 if (!is_scaling_state_different(...)) continue; * 4882 update_stream_scaling_settings(&...crtc->mode, dm_state, * 4883 dm_new_crtc_state->stream); <-- NULL * 4885 if (!dm_new_crtc_state->stream) continue; <-- guard is TOO LATE * 2479 dst.width = stream->timing.h_addressable; <-- NULL DEREF * * Struct prefixes below replicate the exact field order of * dc_stream_state (dc/dc_stream.h: sink, timing, ...) and dc_crtc_timing * (dc/dc_hw_types.h:717: h_total, h_border_left, h_addressable, ...) so the * faulting address equals the kernel's: offset 0x10 on amd64. * * Expected: SIGSEGV reading address 0x10 โ the same page-0 fault the kernel * takes (panic), because no page is mapped at that address in the kernel or * here. */ #include <stdio.h> #include <stddef.h> #include <stdint.h> #include <stdlib.h> /* verbatim prefix of struct dc_crtc_timing, dc/dc_hw_types.h:717 */ struct dc_crtc_timing { uint32_t h_total; uint32_t h_border_left; uint32_t h_addressable; uint32_t h_border_right; uint32_t h_front_porch; uint32_t h_sync_width; uint32_t v_total; uint32_t v_border_top; uint32_t v_addressable; uint32_t v_border_bottom; }; /* verbatim prefix of struct dc_stream_state, dc/dc_stream.h */ struct dc_sink; struct dc_stream_state { struct dc_sink *sink; struct dc_crtc_timing timing; }; struct rect { int x, y, width, height; }; /* models amdgpu_dm.c:2463-2516 (update_stream_scaling_settings) */ static void update_stream_scaling_settings(const int *mode, const int *underscan_enable, struct dc_stream_state *stream) { struct rect src = {0}, dst = {0}; if (!mode) /* `if (!mode) return;` โ in-kernel `mode` is * &crtc->mode, an embedded struct: NEVER NULL */ return; src.width = mode[0]; /* hdisplay */ src.height = mode[1]; /* vdisplay */ dst.width = stream->timing.h_addressable; /* amdgpu_dm.c:2479 <-- */ dst.height = stream->timing.v_addressable; /* amdgpu_dm.c:2480 <-- */ printf("unreachable (kernel would have faulted): dst=%dx%d\n", dst.width, dst.height); } int main(void) { /* * Commit N+3 state (see VERDICT.md): * connector attached to CRTC (enable=1, active=0), mode set, * needs_modeset == false, is_scaling_state_different == true, * dm_new_crtc_state->stream == NULL (never created: active==0). */ int crtc_mode[2] = { 1920, 1080 }; int underscan = 1; struct dc_stream_state *dm_new_crtc_state_stream = NULL; printf("offsetof(dc_stream_state, timing.h_addressable) = 0x%zx\n", offsetof(struct dc_stream_state, timing.h_addressable)); printf("commit-tail scaling loop: acrtc!=NULL, needs_modeset=0, " "scaling_changed=1, stream=NULL\n"); printf("-> update_stream_scaling_settings(mode, dm_state, stream=NULL)\n"); fflush(stdout); update_stream_scaling_settings(crtc_mode, &underscan, dm_new_crtc_state_stream); printf("NOT REACHED\n"); return 0; } |