DF-4047 / null_helper_dispatch.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 126 127 128 129 130 131 132 133 134 135 | /* * null_helper_dispatch.c - userland model of the DF-4047 dispatch: * crafted/corrupt VBIOS command revisions on DCE10/11/11.2 select * command_table.c handlers whose first helper-callback is NULL in the * DCE110/DCE112 command_table_helper tables -> kernel NULL function * pointer call. * * Models (source of truth, this tree): * - sys/dev/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.c * .assign_control_parameter = NULL * .clock_source_id_to_ref_clk_src = NULL * .transmitter_bp_to_atom = NULL * - dce112/command_table_helper_dce112.c: same three NULLs * - dce80/command_table_helper_dce80.c: none NULL * - command_table.c dispatch: * init_dig_encoder_control :120-141 (DIGx rev not in {2,4,5} -> v1 * path; DIG1/DIG2 rev 1 -> dig1/dig2_v1) * encoder_control_dig1_v1 :197-210 (:204 assign_control_parameter) * encoder_control_dig2_v1 :212-225 (:219 assign_control_parameter) * init_transmitter_control :367-396 (crev 2 -> v2, 4 -> v4; crev is * UNINITIALIZED stack if the command * is absent - amdgpu_atom_parse_cmd_header * returns false without writing it, * atom.c:1405-1420) * transmitter_control_v2 :398-516 (:507 transmitter_bp_to_atom) * transmitter_control_v4 :652-773 (:678 clock_source_id_to_ref_clk_src) */ #include <stdio.h> typedef int (*helper_fn)(void); struct command_table_helper { helper_fn controller_id_to_atom; helper_fn encoder_action_to_atom; helper_fn engine_bp_to_atom; helper_fn clock_source_id_to_atom; helper_fn clock_source_id_to_atom_phy_clk_src_id; helper_fn signal_type_to_atom_dig_mode; helper_fn hpd_sel_to_atom; helper_fn dig_encoder_sel_to_atom; helper_fn phy_id_to_atom; helper_fn disp_power_gating_action_to_atom; helper_fn assign_control_parameter; helper_fn clock_source_id_to_ref_clk_src; helper_fn transmitter_bp_to_atom; helper_fn encoder_id_to_atom; helper_fn encoder_mode_bp_to_atom; }; static int stub(void) { return 0; } /* dce80: fully populated (dce80/command_table_helper_dce80.c) */ static const struct command_table_helper helper_dce80 = { stub, stub, stub, stub, stub, stub, stub, stub, stub, stub, stub, stub, stub, stub, stub }; /* dce110 (DCE10, DCE11.0) and dce112 (DCE11.2/11.22): three NULLs */ static const struct command_table_helper helper_dce110 = { stub, stub, stub, stub, stub, stub, stub, stub, stub, stub, NULL, /* .assign_control_parameter */ NULL, /* .clock_source_id_to_ref_clk_src */ NULL, /* .transmitter_bp_to_atom */ stub, stub }; /* returns the name of the NULL helper the modeled call site would hit, * or NULL if the selected path never calls a NULL helper */ static const char * model_encoder_control(const struct command_table_helper *h, int digx_rev, int dig1_rev) { /* init_dig_encoder_control, command_table.c:120-141 */ if (digx_rev == 2 || digx_rev == 4 || digx_rev == 5) return NULL; /* digx_v3/v4/v5: no NULL helpers */ /* default -> init_encoder_control_dig_v1, command_table.c:153-168 */ if (dig1_rev == 1 && h->assign_control_parameter == NULL) return "assign_control_parameter"; /* :204 via encoder_control_dig1_v1 */ return NULL; } static const char * model_transmitter_control(const struct command_table_helper *h, int crev) { /* init_transmitter_control, command_table.c:375-395 */ switch (crev) { case 2: return h->transmitter_bp_to_atom == NULL ? "transmitter_bp_to_atom" : NULL; /* :507, v2 */ case 3: return NULL; /* v3 uses non-NULL helpers */ case 4: return h->clock_source_id_to_ref_clk_src == NULL ? "clock_source_id_to_ref_clk_src" : NULL; /* :678, v4 */ case 5: case 6: return NULL; /* v1_5/v1_6: non-NULL */ default: return NULL; /* handler left NULL */ } } int main(void) { const char *e, *t; printf("== DF-4047 model: crafted-VBIOS revision -> NULL helper call ==\n"); printf("(call *NULL)(...) in the kernel == immediate page-fault panic)\n\n"); e = model_encoder_control(&helper_dce110, 0, 1); printf("DCE11.0, DIGx rev absent/0 + DIG1 crev=1 -> %s\n", e ? e : "no NULL call"); t = model_transmitter_control(&helper_dce110, 2); printf("DCE11.0, UNIPHYTransmitterControl crev=2 -> %s\n", t ? t : "no NULL call"); t = model_transmitter_control(&helper_dce110, 4); printf("DCE11.0, UNIPHYTransmitterControl crev=4 -> %s\n", t ? t : "no NULL call"); e = model_encoder_control(&helper_dce110, 0, 1); printf("DCE11.2, DIGx rev 0 + DIG2 crev=1 -> %s\n", e ? e : "no NULL call"); /* sanity: honest DCE11 VBIOS (DIGx v4/v5, UNIPHY v5/v6) and DCE80 */ printf("DCE11.0 honest (DIGx v4, UNIPHY v5) -> %s\n", (model_encoder_control(&helper_dce110, 4, 1) || model_transmitter_control(&helper_dce110, 5)) ? "NULL!" : "safe (no NULL call)"); printf("DCE8.0 crafted (DIGx 0/DIG1 v1, UNIPHY v2/v4) -> %s\n", (model_encoder_control(&helper_dce80, 0, 1) || model_transmitter_control(&helper_dce80, 2) || model_transmitter_control(&helper_dce80, 4)) ? "NULL!" : "safe (dce80 table fully populated)"); printf("\nexpected NULL-call sites in command_table.c:\n"); printf(" :204 bp->cmd_helper->assign_control_parameter (encoder_control_dig1_v1)\n"); printf(" :219 bp->cmd_helper->assign_control_parameter (encoder_control_dig2_v1)\n"); printf(" :507 bp->cmd_helper->transmitter_bp_to_atom (transmitter_control_v2)\n"); printf(" :678 cmd->clock_source_id_to_ref_clk_src (transmitter_control_v4)\n"); printf(" :367-375 uninit 'crev' steering when UNIPHY cmd absent (CWE-457)\n"); return 0; } |