โฌข DragonFlyBSD Kernel Audit
DF-4601 / construct_order.c
โ† back to finding โ†“ download raw
/*
 * DF-4601 demonstrator: dce112_compressor_construct() initialization-order
 * defect, mirrored statement-for-statement from
 * sys/dev/drm/amd/display/dc/dce112/dce112_compressor.c:787-829 (construct)
 * and :653-724 (program_lpt_control switch).
 *
 * Proves, without AMD hardware:
 *  (1) the `memory_bus_width == 64` single-DRAM-channel guard at :802 is
 *      evaluated while the field is still 0 (kzalloc) โ€” it is assigned from
 *      asic_id.vram_width only afterwards, at :810 โ€” so LPT_SUPPORT is never
 *      cleared on 64-bit-bus ASICs (dead guard);
 *  (2) `lpt_channels_num = 1` at :797 is unconditionally overwritten to 0 at
 *      :818, so the only valid `case 1:` of the
 *      dce112_compressor_program_lpt_control() switch (:675-691) can never
 *      execute โ€” LOW_POWER_TILING_MODE is never programmed by this driver.
 *
 * Build & run on the host (no GPU required):  cc -O2 -o construct_order construct_order.c && ./construct_order
 * Exit 0 = defect confirmed (both proofs hold).
 */
#include <stdint.h>
#include <stdio.h>
#include <string.h>

/* --- mirror of struct compressor fields used by construct() --- */
struct options {
	struct { uint32_t FBC_SUPPORT:1, LPT_SUPPORT:1, DUMMY_BACKEND:1,
			CLK_GATING_DISABLED:1; } bits;
	uint32_t raw;
};

struct compressor {
	struct options options;
	uint32_t memory_bus_width, banks_num, raw_size,
		channel_interleave_size, dram_channels_num, lpt_channels_num;
};

struct dc_ctx { uint32_t vram_width; };

/* exact mirror of dce112_compressor.c:787-829 */
static void
dce112_compressor_construct(struct compressor *compressor,
	struct dc_ctx *ctx)
{
	/* kzalloc'd by dce112_compressor_create(): all fields start 0 */
	compressor->options.raw = 0;
	compressor->options.bits.FBC_SUPPORT = 1;
	compressor->options.bits.LPT_SUPPORT = 1;
	/* For DCE 11 always use one DRAM channel for LPT */
	compressor->lpt_channels_num = 1;			/* :797 */
	compressor->options.bits.DUMMY_BACKEND = 0;

	/* :802 โ€” BUG: memory_bus_width is still 0 here */
	if (compressor->memory_bus_width == 64)
		compressor->options.bits.LPT_SUPPORT = 0;	/* DEAD */

	compressor->options.bits.CLK_GATING_DISABLED = 0;

	compressor->memory_bus_width = ctx->vram_width;		/* :810 */
	compressor->banks_num = 0;
	compressor->raw_size = 0;
	compressor->channel_interleave_size = 0;
	compressor->dram_channels_num = 0;
	compressor->lpt_channels_num = 0;			/* :818 wipes :797 */
}

/* exact mirror of the switch at dce112_compressor.c:675-691 */
static int
lpt_mode_programmed(struct compressor *compressor)
{
	switch (compressor->lpt_channels_num) {
	case 1:
		return 1;	/* LOW_POWER_TILING_MODE would be set to 0 */
	default:
		return 0;	/* warning; mode keeps stale HW value */
	}
}

int
main(void)
{
	struct dc_ctx ctx64 = { .vram_width = 64 };	/* 1-channel system */
	struct compressor cp;
	int fail = 0;

	/* dce112_compressor_create() uses kzalloc: zeroed object */
	memset(&cp, 0, sizeof(cp));
	dce112_compressor_construct(&cp, &ctx64);

	/* Proof 1: the 64-bit-bus guard never fired */
	if (cp.memory_bus_width == 64 && cp.options.bits.LPT_SUPPORT != 0) {
		printf("PROOF-1 OK: memory_bus_width==%u (64-bit bus), "
		    "but LPT_SUPPORT still %u โ€” guard at :802 is dead "
		    "(field assigned at :810, after the check)\n",
		    cp.memory_bus_width, cp.options.bits.LPT_SUPPORT);
	} else {
		printf("PROOF-1 FAILED (guard fired?)\n");
		fail = 1;
	}

	/* Proof 2: lpt_channels_num wiped to 0 -> switch case 1 dead */
	if (cp.lpt_channels_num == 0 && !lpt_mode_programmed(&cp)) {
		printf("PROOF-2 OK: lpt_channels_num==%u at use โ€” "
		    "case 1 at :678 unreachable, LOW_POWER_TILING_MODE "
		    "never programmed (falls to default at :686)\n",
		    cp.lpt_channels_num);
	} else {
		printf("PROOF-2 FAILED\n");
		fail = 1;
	}

	/* Knock-on: with banks_num/raw_size/dram_channels_num all 0 (never
	 * set anywhere in the tree), lpt_size_alignment() == 0, so
	 * rows_per_channel stays 0 (:708) and the low_part alignment at
	 * :500-505 is skipped โ€” enable_lpt() arms LPT with an all-default
	 * config whenever FBC+LPT come up on eDP. */
	printf("KNOCK-ON: lpt_size_alignment = %u * %u * %u = 0 -> "
	    "ROWS_PER_CHAN=0, addr alignment skipped, yet "
	    "LOW_POWER_TILING_ENABLE=1 at :645-650 when LPT comes up\n",
	    cp.raw_size, cp.banks_num, cp.dram_channels_num);

	printf(fail ? "RESULT: NOT REPRODUCED\n" : "RESULT: DEFECT CONFIRMED\n");
	return (fail);
}