⬢ DragonFlyBSD Kernel Audit
DF-5000 / trigger_nullcall.c
← back to finding ↓ download raw
/*
 * 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;
}