/*
 * dp_link_logic_demo.c -- DF-4621 supporting logic demonstration
 *
 * The vulnerable file (sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c)
 * is NOT COMPILED in DragonFly (absent from sys/dev/drm/amd/amdgpu/Makefile
 * SRCS lines 195-201) and its only callers are #if defined(CONFIG_DEBUG_FS)
 * (amdgpu_dm.c:478-479,4119-4120), so the kernel path cannot run on this
 * guest (also: no AMD GPU).  This demo transcribes the *validation logic*
 * 1:1 (amdgpu_dm_debugfs.c:195-221) and the consumer loop 1:1
 * (amdgpu_dm_debugfs.c:394-410) to show, at runtime, that:
 *
 *   1. input "5 a" (lane_count 5 = INVALID, link_rate 0xa = valid) passes
 *      the single-flag `valid_input' check;
 *   2. the invalid lane_count is stored verbatim by
 *      dc_link_set_preferred_link_settings (dc/core/dc.c:460-491 stores
 *      without validation at :484);
 *   3. dp_phy_settings_write's apply loop (:403-410) then writes
 *      lane_settings[4] -- past lane_settings[LANE_COUNT_DP_MAX = 4]
 *      (include/link_service_types.h:71-75, dc/dc_dp_types.h:37).
 *
 * Transcription is asserted line-by-line in the comments; it is a logic
 * proof, NOT a verbatim compile (the kernel build is dead code).
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

/* enum dc_lane_count (dc/dc_dp_types.h:29-37) */
#define LANE_COUNT_UNKNOWN 0
#define LANE_COUNT_ONE 1
#define LANE_COUNT_TWO 2
#define LANE_COUNT_FOUR 4
#define LANE_COUNT_DP_MAX LANE_COUNT_FOUR

/* link rates (subset, values from dc_dp_types.h) */
#define LINK_RATE_UNKNOWN 0
#define LINK_RATE_LOW 6
#define LINK_RATE_HIGH 10 /* 0xa */

struct dc_link_settings { int lane_count, link_rate, link_spread; };
struct dc_lane_settings { int VOLTAGE_SWING, PRE_EMPHASIS, POST_CURSOR2; };
struct link_training_settings {		/* include/link_service_types.h:71-75 */
	struct dc_link_settings link_settings;
	struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX];
	bool allow_invalid_msa_timing_param;
	int canary;			/* harness: detect writes past struct */
};
struct dc_link {
	struct dc_link_settings preferred_link_setting;
	int test_pattern_enabled;
};

/* ---- transcribed from amdgpu_dm_debugfs.c:195-221 ---- */
static bool validate(long param0, long param1)
{
	bool valid_input = false;

	switch (param0) {
	case LANE_COUNT_ONE:
	case LANE_COUNT_TWO:
	case LANE_COUNT_FOUR:
		valid_input = true;
		break;
	default:
		break;
	}
	switch (param1) {
	case LINK_RATE_LOW:
	case LINK_RATE_HIGH:
		valid_input = true;	/* BUG: OR-combined, not AND */
		break;
	default:
		break;
	}
	return valid_input;
}

int main(void)
{
	long param[2] = {5, 0xa};	/* echo "5 a" > link_settings */
	struct dc_link link = {{0, LINK_RATE_UNKNOWN, 0}, 1};
	struct link_training_settings lts;
	struct dc_link_settings prefer;
	int r;

	printf("input: lane_count=5 (invalid: not 1/2/4), link_rate=0xa (valid)\n");
	if (!validate(param[0], param[1])) {
		printf("rejected (fix behaves this way) -- no finding\n");
		return 1;
	}
	printf("ACCEPTED by the single valid_input flag "
	       "(amdgpu_dm_debugfs.c:195-221)\n");

	/* dc.c:484 stores verbatim: */
	prefer.lane_count = (int)param[0];
	prefer.link_rate = (int)param[1];
	prefer.link_spread = 0;
	link.preferred_link_setting = prefer;
	printf("stored preferred_link_setting.lane_count = %d "
	       "(dc.c:484, unvalidated)\n", link.preferred_link_setting.lane_count);

	/* dp_phy_settings_write:386-400 (use_prefer path) + :403-410 loop */
	memset(&lts, 0, sizeof(lts));
	lts.link_settings.lane_count = link.preferred_link_setting.lane_count;
	for (r = 0; r < lts.link_settings.lane_count; r++) {
		/* in the kernel: lane_settings[r] with r == 4 is OOB (array
		 * of LANE_COUNT_DP_MAX = 4).  Here r==4 writes past the
		 * array into allow_invalid_msa_timing_param + canary. */
		struct dc_lane_settings *p = &lts.lane_settings[r];
		p->VOLTAGE_SWING = 0; p->PRE_EMPHASIS = 1; p->POST_CURSOR2 = 2;
		if (r >= LANE_COUNT_DP_MAX)
			printf("loop iteration r=%d: OOB write at "
			       "&lane_settings[%d] -- past "
			       "lane_settings[LANE_COUNT_DP_MAX-1=%d]\n",
			       r, r, LANE_COUNT_DP_MAX - 1);
	}
	printf("canary=%d allow_invalid_msa_timing_param overwritten=%d "
	       "(stack OOB in kernel: adjacent frame data)\n",
	       lts.canary, (int)lts.allow_invalid_msa_timing_param);
	printf("=> with lane_count 0xffffffff the loop is unbounded "
	       "(linear stack smash)\n");
	return 0;
}
