DF-5000 / trigger_nullcall.c
/* * DF-5000 semantics demo (host userspace, NOT a kernel exploit). * * Replicates, byte-for-byte in shape, the kernel pattern under audit: * * sys/dev/drm/amd/display/dc/bios/command_table_helper_struct.h:33-63 * struct command_table_helper { ... 17 function pointers ... }; * * sys/dev/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.c:329-349 * static const struct command_table_helper command_table_helper_funcs = { * ...15 designated initializers; dc_clock_type_to_atom and * transmitter_color_depth_to_atom are NOT listed... * }; * * sys/dev/drm/amd/display/dc/bios/command_table.c:1188 (set_pixel_clock_v7) * clk.ucDeepColorRatio = (uint8_t) bp->cmd_helper-> * transmitter_color_depth_to_atom(bp_params->color_depth); * ... no NULL check on the member ... * * C99 6.7.8p10: unnamed members of a designated-initializer aggregate are * zero-initialized. Calling through the zero-filled member pointer is UB and * on this platform faults at offsetof(struct, member). * * Expected: "FAULT: called through NULL member at offset ..." then SIGSEGV. */ #include <stdio.h> #include <stdint.h> #include <stddef.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> struct command_table_helper { int (*m00)(void); int (*m01)(void); int (*m02)(void); int (*m03)(void); int (*m04)(void); int (*m05)(void); int (*m06)(void); int (*m07)(void); int (*m08)(void); int (*m09)(void); int (*m10)(void); int (*m11)(void); int (*m12)(void); int (*m13)(void); int (*m14)(void); /* the two members dce80 never initializes (last two in the real struct) */ uint8_t (*dc_clock_type_to_atom)(void); uint8_t (*transmitter_color_depth_to_atom)(int color_depth); }; static int impl_m00(void) { return 0; } /* ... stand-ins for the 15 implemented members ... */ static const struct command_table_helper command_table_helper_funcs = { .m00 = impl_m00, /* only 15 of 17 initialized, exactly like dce80:329-349 */ }; static void segv(int sig, siginfo_t *si, void *uc) { (void)sig; (void)uc; printf("FAULT: called through NULL member at address %p " "(offsetof transmitter_color_depth_to_atom = 0x%lx)\n", si->si_addr, (unsigned long)offsetof(struct command_table_helper, transmitter_color_depth_to_atom)); printf("SEMANTICS DEMO PASSED: unguarded indirect call through the " "zero-filled vtable member faults.\n"); fflush(stdout); _exit(42); } int main(void) { struct sigaction sa = {0}; sa.sa_sigaction = segv; sa.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &sa, NULL); printf("dc_clock_type_to_atom = %p\n", (void *)(uintptr_t)command_table_helper_funcs.dc_clock_type_to_atom); printf("transmitter_color_depth_to_atom= %p <- must be NULL (C99 6.7.8p10)\n", (void *)(uintptr_t)command_table_helper_funcs.transmitter_color_depth_to_atom); /* the exact shape of command_table.c:1188 / command_table2.c:308-311 */ printf("calling set_pixel_clock_v7 line-1188 shape...\n"); uint8_t deep = command_table_helper_funcs.transmitter_color_depth_to_atom(8); printf("unreachable, got %u\n", deep); return 0; } |