DF-3957 / trigger_df3957.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 | /* * DF-3957 โ radeon atombios_encoders.c unguarded enc_priv derefs for * LCD-supporting digital encoders (NULL-page read AND write) * ===================================================================== * Target: sys/dev/drm/radeon/atombios_encoders.c (pass 2; pass 1: clean) * * Root cause: radeon_add_atom_encoder() (:2834-2838, :2868-2872) assigns * enc_priv = radeon_atombios_get_lvds_info() for LCD-supporting encoders. * That function (radeon_atombios.c:1617-1755) returns NULL when the VBIOS * LVDS_Info DATA table is missing (atom_parse_data_header fails) or kzalloc * fails โ while the connector table still advertised ATOM_DEVICE_LCD support * (crafted/marginal VBIOS, or OOM at boot). Contrast the guards that DO * exist: :100 (set_backlight_level), :240 (backlight_init), :629 * (digital_setup), :2551 (prepare). * * Unguarded sinks (radeon_encoder->enc_priv dereferenced with no NULL check): * :742-744 atombios_get_encoder_mode: dig_enc->active_mst_links (READ) * :1085 atombios_dig_transmitter_setup2: dig->dig_encoder (READ; * reached at BOOT from radeon_atom_encoder_init :2314) * :1695-97 dpms_avivo: dig->backlight_level (READ, arg evaluation) * :1744 dpms_dig: dig->panel_mode = ... (WRITE) * :1783 dpms_dig: dig->backlight_level (READ) * :1796 dpms_dig: dig->active_mst_links (READ) * :2006-07 atombios_set_encoder_crtc_source: dig->dig_encoder (READ) * :2270-72 radeon_atom_pick_dig_encoder: dig->dig_encoder (READ) * :2661-63 radeon_atom_encoder_disable: dig->dig_encoder (READ) * then dig->dig_encoder = -1 (WRITE at NULL+offset) * * All faults land in the NULL page (offsets < ~0x60) => kernel page fault => * panic. Upstream Linux radeon carries the same unguarded paths (inherited); * trigger is hardware+VBIOS-gated (no unprivileged local vector). * * This harness replicates struct offsets and executes the verbatim * NULL+offset read and WRITE in a child; arithmetic demonstration, NOT a * kernel reproduction. * * Build: cc -O2 -Wall -o trigger_df3957 trigger_df3957.c * Run: ./trigger_df3957 / ./trigger_df3957 crash */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stddef.h> #include <unistd.h> #include <sys/wait.h> #include <signal.h> /* struct radeon_encoder_atom_dig (radeon.h) โ only the leading fields matter */ struct radeon_encoder_atom_dig { int dig_encoder; /* first member, offset 0 */ int linkb; int active_mst_links; int panel_mode; int backlight_level; }; int main(int argc, char **argv) { int crash = (argc > 1 && !strcmp(argv[1], "crash")); printf("DF-3957 radeon atombios_encoders enc_priv==NULL family (simulation)\n"); printf("enc_priv = radeon_atombios_get_lvds_info() -> NULL (missing LVDS_Info table / OOM)\n"); printf(":742 dig_enc->active_mst_links READ at NULL+%zu\n", offsetof(struct radeon_encoder_atom_dig, active_mst_links)); printf(":1085 dig->dig_encoder READ at NULL+%zu\n", offsetof(struct radeon_encoder_atom_dig, dig_encoder)); printf(":1744 dig->panel_mode = ... WRITE at NULL+%zu\n", offsetof(struct radeon_encoder_atom_dig, panel_mode)); printf(":2663 dig->dig_encoder = -1 WRITE at NULL+%zu\n", offsetof(struct radeon_encoder_atom_dig, dig_encoder)); printf("kernel effect: NULL-page read/write => page fault => panic\n"); if (!crash) { printf("NON-CRASH MODE: analysis only. Re-run with 'crash' to execute them.\n"); return 0; } struct radeon_encoder_atom_dig *dig = NULL; /* enc_priv == NULL */ pid_t p1 = fork(); if (p1 == 0) { /* radeon_atom_encoder_disable (:2661-2663) */ printf("child1: :2661-2663 read dig->dig_encoder, then WRITE -1 ...\n"); fflush(stdout); int v = dig->dig_encoder; /* READ */ dig->dig_encoder = -1; /* WRITE */ printf("child1: NOT REACHED (v=%d)\n", v); _exit(0); } pid_t p2 = fork(); if (p2 == 0) { /* radeon_atom_encoder_dpms_dig (:1744) */ printf("child2: :1744 WRITE dig->panel_mode ...\n"); fflush(stdout); dig->panel_mode = 1; printf("child2: NOT REACHED\n"); _exit(0); } int st, rc = 0; waitpid(p1, &st, 0); printf("child1 (disable :2661-2663): %s", WIFSIGNALED(st) ? "SIGNALLED" : "exited"); if (WIFSIGNALED(st)) printf(" sig=%d", WTERMSIG(st)); printf("\n"); if (!(WIFSIGNALED(st) && WTERMSIG(st) == SIGSEGV)) rc |= 1; waitpid(p2, &st, 0); printf("child2 (dpms_dig :1744): %s", WIFSIGNALED(st) ? "SIGNALLED" : "exited"); if (WIFSIGNALED(st)) printf(" sig=%d", WTERMSIG(st)); printf("\n"); if (!(WIFSIGNALED(st) && WTERMSIG(st) == SIGSEGV)) rc |= 2; printf("RESULT: %s (rc=%d)\n", rc ? "DEVIATION" : "NULL-PAGE FAULTS AS PREDICTED", rc); return rc; } |