⬢ DragonFlyBSD Kernel Audit
DF-4912 / stats_len0_demo.c
← back to finding ↓ download raw
/*
 * DF-4912 — standalone extraction of mod_stats_update_event's exact index
 * arithmetic (sys/dev/drm/amd/display/modules/stats/stats.c:296-326) to
 * demonstrate the length==0 off-by-one write at event_string[-1].
 *
 * The kernel module itself is NOT compiled on DragonFly
 * (sys/dev/drm/amd/amdgpu/Makefile builds freesync/color/info_packet only),
 * so this demo is the only executable form of the logic.
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>

#define MOD_STATS_EVENT_STRING_MAX 512

struct stats_event_cache {
	unsigned int entry_id;
	char event_string[MOD_STATS_EVENT_STRING_MAX];
};

static struct stats_event_cache ev;
static unsigned int event_index, entry_id;

/* byte-exact clone of stats.c:316-325 */
static void mod_stats_update_event(const char *event_string, unsigned int length)
{
	unsigned int copy_length = 0;

	copy_length = length;
	if (length > MOD_STATS_EVENT_STRING_MAX)
		copy_length = MOD_STATS_EVENT_STRING_MAX;

	memcpy(&ev.event_string, event_string, copy_length);
	ev.event_string[copy_length - 1] = '\0';   /* <- [-1] when length==0 */

	ev.entry_id = entry_id;
	event_index++;
	entry_id++;
}

int main(void)
{
	unsigned char *base = (unsigned char *)&ev;
	ptrdiff_t idx;

	printf("event_string at offset %td within struct\n",
	       (unsigned char *)ev.event_string - base);

	/* pre-fill so we can observe the OOB write */
	ev.entry_id = 0xAABBCCDD;
	memset(ev.event_string, 'A', sizeof(ev.event_string));

	mod_stats_update_event("", 0);

	/* where did the NUL land? */
	idx = 3; /* high byte of entry_id on little-endian */
	printf("after length==0 call: entry_id = 0x%08x\n", ev.entry_id);
	if (((unsigned char *)&ev.entry_id)[idx] == 0x00 &&
	    ev.event_string[0] == 'A')
		printf("PROVEN: write landed at event_string[-1] (offset 3, "
		       "entry_id MSB); byte of event_string[0] untouched\n");
	else
		printf("write pattern differs; inspect manually\n");

	/* demonstrate the intended-guard behavior too (fixed semantics) */
	printf("note: stats.c:323 then overwrites entry_id wholesale, "
	       "clobbering the OOB byte — net-zero corruption, hence Info\n");
	return 0;
}