⬢ DragonFlyBSD Kernel Audit
DF-5668 / fix.diff
← back to finding ↓ download raw
--- sys/platform/pc64/acpica/OsdEnvironment.c
+++ sys/platform/pc64/acpica/OsdEnvironment.c
@@ -33,12 +33,17 @@
 
 #include <sys/types.h>
 #include <sys/linker_set.h>
+#include <sys/systm.h>
 #include <sys/sysctl.h>
 
+#include <machine/pmap.h>
 #include <machine/vmparam.h>
 
 #include "acpi.h"
 
+static int	acpi_rsdp_checksum(const uint8_t *, int);
+static int	acpi_validate_rsdp_paddr(u_long);
+
 static u_long acpi_root_phys;
 
 SYSCTL_ULONG(_machdep, OID_AUTO, acpi_root, CTLFLAG_RD, &acpi_root_phys, 0,
@@ -56,6 +61,51 @@
 	return (AE_OK);
 }
 
+/*
+ * Sum a region modulo 256; a valid ACPI checksum region sums to 0.
+ */
+static int
+acpi_rsdp_checksum(const uint8_t *cp, int len)
+{
+	int sum = 0;
+
+	while (len-- > 0)
+		sum += *cp++;
+	return (sum & 0xFF);
+}
+
+/*
+ * The RSDP address passed in via hint.acpi.0.rsdp must refer to actual
+ * physical memory (mapping an address with reserved PTE bits faults the
+ * moment it is read) and the bytes it points at must actually look like
+ * an RSDP (signature, a revision we understand, checksums) before it is
+ * handed to ACPICA as the table root.  Otherwise fall back to the
+ * firmware scan.
+ */
+static int
+acpi_validate_rsdp_paddr(u_long paddr)
+{
+	const ACPI_TABLE_RSDP *rsdp;
+	void *map;
+	int ok = 0;
+
+	if (paddr == 0 || paddr >= ptoa(Maxmem))
+		return (0);
+	map = pmap_mapdev(paddr, PAGE_SIZE);
+	rsdp = (const ACPI_TABLE_RSDP *)map;
+	if (memcmp(rsdp->Signature, ACPI_SIG_RSDP,
+	    sizeof(rsdp->Signature)) == 0 &&
+	    rsdp->Revision <= 2 &&
+	    acpi_rsdp_checksum((const uint8_t *)rsdp,
+	    ACPI_RSDP_CHECKSUM_LENGTH) == 0 &&
+	    (rsdp->Revision < 2 ||
+	    acpi_rsdp_checksum((const uint8_t *)rsdp,
+	    ACPI_RSDP_XCHECKSUM_LENGTH) == 0))
+		ok = 1;
+	pmap_unmapdev((vm_offset_t)map, PAGE_SIZE);
+	return (ok);
+}
+
 ACPI_PHYSICAL_ADDRESS
 AcpiOsGetRootPointer(void)
 {
@@ -70,8 +120,14 @@
 		 * manually here.
 		 */
 		if (kgetenv_ulong("hint.acpi.0.rsdp", &acpi_root) == 1) {
-			acpi_root_phys = acpi_root;
-		} else {
+			if (acpi_validate_rsdp_paddr(acpi_root)) {
+				acpi_root_phys = acpi_root;
+			} else {
+				kprintf("AcpiOsGetRootPointer: kenv RSDP failed "
+				    "validation, falling back to firmware scan\n");
+			}
+		}
+		if (acpi_root_phys == 0) {
 			status = AcpiFindRootPointer(&ptr);
 			if (ACPI_SUCCESS(status))
 				acpi_root_phys = ptr;
--- sys/contrib/dev/acpica/source/components/tables/tbxfload.c
+++ sys/contrib/dev/acpica/source/components/tables/tbxfload.c
@@ -272,10 +272,16 @@
      * Load the namespace. The DSDT is required, but any SSDT and
      * PSDT tables are optional. Verify the DSDT.
      */
+    if (!AcpiGbl_RootTableList.CurrentTableCount ||
+        AcpiGbl_DsdtIndex == ACPI_INVALID_TABLE_INDEX)
+    {
+        Status = AE_NO_ACPI_TABLES;
+        goto UnlockAndExit;
+    }
+
     Table = &AcpiGbl_RootTableList.Tables[AcpiGbl_DsdtIndex];
 
-    if (!AcpiGbl_RootTableList.CurrentTableCount ||
-        !ACPI_COMPARE_NAMESEG (Table->Signature.Ascii, ACPI_SIG_DSDT) ||
+    if (!ACPI_COMPARE_NAMESEG (Table->Signature.Ascii, ACPI_SIG_DSDT) ||
          ACPI_FAILURE (AcpiTbValidateTable (Table)))
     {
         Status = AE_NO_ACPI_TABLES;