⬢ DragonFlyBSD Kernel Audit
DF-4731 / df4731_proof.c
← back to finding ↓ download raw
/*
 * DF-4731 proof-of-logic harness (userland, no GPU required).
 *
 * Extracts verbatim:
 *   - enum dc_irq_source        (sys/dev/drm/amd/display/dc/irq_types.h:40-148)
 *   - DAL_VALID_IRQ_SRC_NUM     (sys/dev/drm/amd/display/dc/irq_types.h:157-158)
 *
 * Demonstrates that DAL_VALID_IRQ_SRC_NUM(DAL_IRQ_SOURCES_NUMBER) == true,
 * i.e. amdgpu_dm_irq.c's validate_irq_registration_params()
 * (sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c:176) accepts
 * irq_source == DAL_IRQ_SOURCES_NUMBER, which amdgpu_dm_irq_register_interrupt()
 * then uses to index:
 *   adev->dm.irq_handler_list_high_tab[irq_source]   (amdgpu_dm_irq.c:238)
 *   adev->dm.irq_handler_list_low_tab[irq_source].head (amdgpu_dm_irq.c:242)
 * arrays declared as [DAL_IRQ_SOURCES_NUMBER]        (amdgpu_dm.h:106-107)
 * -> one-past-the-end struct access.
 *
 * Build: cc -O2 -o df4731_proof df4731_proof.c
 * Run:   ./df4731_proof
 */
#include <stdio.h>
#include <stdbool.h>

/* ---- verbatim from sys/dev/drm/amd/display/dc/irq_types.h ---- */
enum dc_irq_source {
	DC_IRQ_SOURCE_INVALID = 0,
	DC_IRQ_SOURCE_HPD1, DC_IRQ_SOURCE_HPD2, DC_IRQ_SOURCE_HPD3,
	DC_IRQ_SOURCE_HPD4, DC_IRQ_SOURCE_HPD5, DC_IRQ_SOURCE_HPD6,
	DC_IRQ_SOURCE_HPD1RX, DC_IRQ_SOURCE_HPD2RX, DC_IRQ_SOURCE_HPD3RX,
	DC_IRQ_SOURCE_HPD4RX, DC_IRQ_SOURCE_HPD5RX, DC_IRQ_SOURCE_HPD6RX,
	DC_IRQ_SOURCE_I2C_DDC1, DC_IRQ_SOURCE_I2C_DDC2, DC_IRQ_SOURCE_I2C_DDC3,
	DC_IRQ_SOURCE_I2C_DDC4, DC_IRQ_SOURCE_I2C_DDC5, DC_IRQ_SOURCE_I2C_DDC6,
	DC_IRQ_SOURCE_DPSINK1, DC_IRQ_SOURCE_DPSINK2, DC_IRQ_SOURCE_DPSINK3,
	DC_IRQ_SOURCE_DPSINK4, DC_IRQ_SOURCE_DPSINK5, DC_IRQ_SOURCE_DPSINK6,
	DC_IRQ_SOURCE_TIMER,
	DC_IRQ_SOURCE_PFLIP_FIRST,
	DC_IRQ_SOURCE_PFLIP1 = DC_IRQ_SOURCE_PFLIP_FIRST,
	DC_IRQ_SOURCE_PFLIP2, DC_IRQ_SOURCE_PFLIP3, DC_IRQ_SOURCE_PFLIP4,
	DC_IRQ_SOURCE_PFLIP5, DC_IRQ_SOURCE_PFLIP6,
	DC_IRQ_SOURCE_PFLIP_UNDERLAY0,
	DC_IRQ_SOURCE_PFLIP_LAST = DC_IRQ_SOURCE_PFLIP_UNDERLAY0,
	DC_IRQ_SOURCE_GPIOPAD0, DC_IRQ_SOURCE_GPIOPAD1, DC_IRQ_SOURCE_GPIOPAD2,
	DC_IRQ_SOURCE_GPIOPAD3, DC_IRQ_SOURCE_GPIOPAD4, DC_IRQ_SOURCE_GPIOPAD5,
	DC_IRQ_SOURCE_GPIOPAD6, DC_IRQ_SOURCE_GPIOPAD7, DC_IRQ_SOURCE_GPIOPAD8,
	DC_IRQ_SOURCE_GPIOPAD9, DC_IRQ_SOURCE_GPIOPAD10, DC_IRQ_SOURCE_GPIOPAD11,
	DC_IRQ_SOURCE_GPIOPAD12, DC_IRQ_SOURCE_GPIOPAD13, DC_IRQ_SOURCE_GPIOPAD14,
	DC_IRQ_SOURCE_GPIOPAD15, DC_IRQ_SOURCE_GPIOPAD16, DC_IRQ_SOURCE_GPIOPAD17,
	DC_IRQ_SOURCE_GPIOPAD18, DC_IRQ_SOURCE_GPIOPAD19, DC_IRQ_SOURCE_GPIOPAD20,
	DC_IRQ_SOURCE_GPIOPAD21, DC_IRQ_SOURCE_GPIOPAD22, DC_IRQ_SOURCE_GPIOPAD23,
	DC_IRQ_SOURCE_GPIOPAD24, DC_IRQ_SOURCE_GPIOPAD25, DC_IRQ_SOURCE_GPIOPAD26,
	DC_IRQ_SOURCE_GPIOPAD27, DC_IRQ_SOURCE_GPIOPAD28, DC_IRQ_SOURCE_GPIOPAD29,
	DC_IRQ_SOURCE_GPIOPAD30,
	DC_IRQ_SOURCE_DC1UNDERFLOW, DC_IRQ_SOURCE_DC2UNDERFLOW,
	DC_IRQ_SOURCE_DC3UNDERFLOW, DC_IRQ_SOURCE_DC4UNDERFLOW,
	DC_IRQ_SOURCE_DC5UNDERFLOW, DC_IRQ_SOURCE_DC6UNDERFLOW,
	DC_IRQ_SOURCE_DMCU_SCP, DC_IRQ_SOURCE_VBIOS_SW,
	DC_IRQ_SOURCE_VUPDATE1, DC_IRQ_SOURCE_VUPDATE2, DC_IRQ_SOURCE_VUPDATE3,
	DC_IRQ_SOURCE_VUPDATE4, DC_IRQ_SOURCE_VUPDATE5, DC_IRQ_SOURCE_VUPDATE6,
	DC_IRQ_SOURCE_VBLANK1, DC_IRQ_SOURCE_VBLANK2, DC_IRQ_SOURCE_VBLANK3,
	DC_IRQ_SOURCE_VBLANK4, DC_IRQ_SOURCE_VBLANK5, DC_IRQ_SOURCE_VBLANK6,
	DC_IRQ_SOURCE_DC1_VLINE0, DC_IRQ_SOURCE_DC2_VLINE0, DC_IRQ_SOURCE_DC3_VLINE0,
	DC_IRQ_SOURCE_DC4_VLINE0, DC_IRQ_SOURCE_DC5_VLINE0, DC_IRQ_SOURCE_DC6_VLINE0,
	DAL_IRQ_SOURCES_NUMBER
};

#define DAL_VALID_IRQ_SRC_NUM(src) \
	((src) <= DAL_IRQ_SOURCES_NUMBER && (src) > DC_IRQ_SOURCE_INVALID)
/* ---- end verbatim ---- */

int main(void)
{
	bool accept_last_valid = DAL_VALID_IRQ_SRC_NUM(DAL_IRQ_SOURCES_NUMBER - 1);
	bool accept_one_past   = DAL_VALID_IRQ_SRC_NUM(DAL_IRQ_SOURCES_NUMBER);
	bool accept_two_past   = DAL_VALID_IRQ_SRC_NUM(DAL_IRQ_SOURCES_NUMBER + 1);
	bool accept_invalid    = DAL_VALID_IRQ_SRC_NUM(DC_IRQ_SOURCE_INVALID);

	printf("enum size      : DAL_IRQ_SOURCES_NUMBER = %d\n", DAL_IRQ_SOURCES_NUMBER);
	printf("array extent   : irq_handler_list_{low,high}_tab[%d] -> valid idx 0..%d\n",
	       DAL_IRQ_SOURCES_NUMBER, DAL_IRQ_SOURCES_NUMBER - 1);
	printf("accept src=%-3d (last valid) : %s\n", DAL_IRQ_SOURCES_NUMBER - 1,
	       accept_last_valid ? "true (correct)" : "false");
	printf("accept src=%-3d (ONE PAST)   : %s   <-- BUG: feeds tab[90] in\n",
	       DAL_IRQ_SOURCES_NUMBER, accept_one_past ? "true" : "false");
	printf("        amdgpu_dm_irq.c:238/:242/:123/:127 (list_add_tail/list walk)\n");
	printf("accept src=%-3d (two past)   : %s\n", DAL_IRQ_SOURCES_NUMBER + 1,
	       accept_two_past ? "true" : "false");
	printf("accept src=%-3d (INVALID)    : %s (correct)\n", DC_IRQ_SOURCE_INVALID,
	       accept_invalid ? "true" : "false");

	if (accept_one_past && !accept_two_past && accept_last_valid) {
		printf("\nRESULT: OFF-BY-ONE CONFIRMED - exactly index == array size passes\n");
		printf("        the guard used by validate_irq_registration_params()\n");
		printf("        and validate_irq_unregistration_params().\n");
		return 0;
	}
	printf("\nRESULT: guard behaved unexpectedly\n");
	return 1;
}