/*
 * DF-4605 control-flow harness. The two #include fragments below are
 * sed-extracted verbatim from the guest's live kernel source:
 *   guard.inc  = amdgpu_acpi.c lines 374-385 (the gutted guard)
 *   deref.inc  = amdgpu_acpi.c line 387      (the first deref)
 * Struct definitions replicate sys/dev/drm/amd/amdgpu/amdgpu_acpi.c:38-75
 * byte-for-byte in size/offset (ACPI_HANDLE = pointer, bool = 1 byte),
 * so `functions.sbios_requests` sits at offset 18 (0x12), exactly as in
 * the kernel build.
 */
#include <stdio.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

typedef unsigned int UINT32;

struct amdgpu_atif_notification_cfg { char enabled; int command_code; };
struct amdgpu_atif_notifications {
	char display_switch, expansion_mode_change, thermal_state,
	     forced_power_state, system_power_state, display_conf_change,
	     px_gfx_switch, brightness_change, dgpu_display_event; /* 9 x 1B */
};
struct amdgpu_atif_functions {
	char system_params, sbios_requests, select_active_disp, lid_state,
	     get_tv_standard, set_tv_standard, get_panel_expansion_mode,
	     set_panel_expansion_mode, temperature_change,
	     graphics_device_types;                               /* 10 x 1B */
};
struct amdgpu_atif {
	void *handle;                          /* ACPI_HANDLE        @0   */
	struct amdgpu_atif_notifications notifications;   /* @8..16     */
	struct amdgpu_atif_functions functions;           /* @17..26    */
	struct amdgpu_atif_notification_cfg notification_cfg;
};

static int fixed_mode = 0;
/*
 * Opaque barrier: prevents the optimizer from proving atif==NULL and
 * deleting the deref as dead code (UB elision). In the kernel, atif comes
 * from adev->atif at notify-dispatch time and is never foldable.
 */
static struct amdgpu_atif * volatile atif_arg = NULL;

__attribute__((noinline))
static void handler_under_test(struct amdgpu_atif *atif, UINT32 type)
{
	if (fixed_mode) {
#include "guard_fixed.inc"
	} else {
#include "guard.inc"
	}
#include "deref.inc"
	/* kernel body (amdgpu_acpi.c:388-431) calls amdgpu_atif_get_sbios_requests()
	 * — a real side effect. The sink replicates that so the compiler cannot
	 * drop the (dead-looking) empty-guard + load. */
	printf("KERNEL-EQUIV: sbios_requests path entered "
	       "(kernel evaluates ATIF GET_SYSTEM_BIOS_REQUESTS here)\n");
	}
}

static void segv(int sig, siginfo_t *si, void *uc)
{
	(void)sig; (void)uc;
	printf("RESULT: SIGSEGV at address %p  "
	       "(=&atif->functions.sbios_requests with atif==NULL, expected 0x12)\n",
	       si->si_addr);
	if (fixed_mode)
		printf("FIXED-MODE FAULT: unexpected — fix did NOT work\n");
	else
		printf("VERDICT: guard body compiled to NOTHING; control fell "
		       "through the if() into the NULL deref\n");
	exit(fixed_mode ? 1 : 42);
}

int main(int argc, char **argv)
{
	struct sigaction sa;
	fixed_mode = (argc > 1);

	memset(&sa, 0, sizeof(sa));
	sa.sa_sigaction = segv;
	sa.sa_flags = SA_SIGINFO;
	sigaction(SIGSEGV, &sa, NULL);

	printf("sizeof(struct amdgpu_atif)=%zu  "
	       "offsetof(functions.sbios_requests)=%zu\n",
	       sizeof(struct amdgpu_atif),
	       (size_t)&((struct amdgpu_atif *)0)->functions.sbios_requests);

	/* exactly the kernel state on ATIF-less systems: adev->atif == NULL.
	 * The NULL is produced at RUNTIME (getenv) so the optimizer cannot
	 * constant-fold it and delete the deref as UB-dead-code (which gcc 8
	 * demonstrably does otherwise — see VERDICT.md; in the kernel atif is
	 * adev->atif, an opaque heap field, equally unfoldable). */
	{
		const char *e = getenv("DF4605_ATIF");
		struct amdgpu_atif *atif =
			(struct amdgpu_atif *)(uintptr_t)(e ? strtol(e, NULL, 0) : 0);
		handler_under_test(atif, 0x81);
	}
	printf("RESULT: no fault — guard returned early (finding REFUTED in this mode)\n");
	return 0;
}
