summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGordon Tetlow <gordon@FreeBSD.org>2019-11-12 18:13:04 +0000
committerGordon Tetlow <gordon@FreeBSD.org>2019-11-12 18:13:04 +0000
commit61ba71b7dbca5170215e39fd3cc55853107844b8 (patch)
tree3930c19047628065cc98f56344c95b54e05af437
parent003f54e19a58bbe585c887803d559844aeaf740b (diff)
downloadsrc-test2-61ba71b7dbca5170215e39fd3cc55853107844b8.tar.gz
src-test2-61ba71b7dbca5170215e39fd3cc55853107844b8.zip
Fix Machine Check Exception on Page Size Change.
Approved by: so Security: FreeBSD-SA-19:25.mcepsc Security: CVE-2018-12207
Notes
Notes: svn path=/releng/12.0/; revision=354653
-rw-r--r--sys/amd64/amd64/pmap.c68
-rw-r--r--sys/amd64/include/pmap.h1
-rw-r--r--sys/dev/cpuctl/cpuctl.c7
-rw-r--r--sys/x86/include/specialreg.h1
4 files changed, 76 insertions, 1 deletions
diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c
index c5ca2627f777..d2d2465dfe97 100644
--- a/sys/amd64/amd64/pmap.c
+++ b/sys/amd64/amd64/pmap.c
@@ -1309,6 +1309,51 @@ pmap_page_init(vm_page_t m)
m->md.pat_mode = PAT_WRITE_BACK;
}
+static int pmap_allow_2m_x_ept;
+SYSCTL_INT(_vm_pmap, OID_AUTO, allow_2m_x_ept, CTLFLAG_RWTUN | CTLFLAG_NOFETCH,
+ &pmap_allow_2m_x_ept, 0,
+ "Allow executable superpage mappings in EPT");
+
+void
+pmap_allow_2m_x_ept_recalculate(void)
+{
+ /*
+ * SKL002, SKL012S. Since the EPT format is only used by
+ * Intel CPUs, the vendor check is merely a formality.
+ */
+ if (!(cpu_vendor_id != CPU_VENDOR_INTEL ||
+ (cpu_ia32_arch_caps & IA32_ARCH_CAP_IF_PSCHANGE_MC_NO) != 0 ||
+ (CPUID_TO_FAMILY(cpu_id) == 0x6 &&
+ (CPUID_TO_MODEL(cpu_id) == 0x26 || /* Atoms */
+ CPUID_TO_MODEL(cpu_id) == 0x27 ||
+ CPUID_TO_MODEL(cpu_id) == 0x35 ||
+ CPUID_TO_MODEL(cpu_id) == 0x36 ||
+ CPUID_TO_MODEL(cpu_id) == 0x37 ||
+ CPUID_TO_MODEL(cpu_id) == 0x86 ||
+ CPUID_TO_MODEL(cpu_id) == 0x1c ||
+ CPUID_TO_MODEL(cpu_id) == 0x4a ||
+ CPUID_TO_MODEL(cpu_id) == 0x4c ||
+ CPUID_TO_MODEL(cpu_id) == 0x4d ||
+ CPUID_TO_MODEL(cpu_id) == 0x5a ||
+ CPUID_TO_MODEL(cpu_id) == 0x5c ||
+ CPUID_TO_MODEL(cpu_id) == 0x5d ||
+ CPUID_TO_MODEL(cpu_id) == 0x5f ||
+ CPUID_TO_MODEL(cpu_id) == 0x6e ||
+ CPUID_TO_MODEL(cpu_id) == 0x7a ||
+ CPUID_TO_MODEL(cpu_id) == 0x57 || /* Knights */
+ CPUID_TO_MODEL(cpu_id) == 0x85))))
+ pmap_allow_2m_x_ept = 1;
+ TUNABLE_INT_FETCH("hw.allow_2m_x_ept", &pmap_allow_2m_x_ept);
+}
+
+static bool
+pmap_allow_2m_x_page(pmap_t pmap, bool executable)
+{
+
+ return (pmap->pm_type != PT_EPT || !executable ||
+ !pmap_allow_2m_x_ept);
+}
+
/*
* Initialize the pmap module.
* Called by vm_init, to initialize any structures that the pmap
@@ -1353,6 +1398,9 @@ pmap_init(void)
}
}
+ /* IFU */
+ pmap_allow_2m_x_ept_recalculate();
+
/*
* Initialize the vm page array entries for the kernel pmap's
* page table pages.
@@ -4823,6 +4871,15 @@ retry:
}
#if VM_NRESERVLEVEL > 0
+static bool
+pmap_pde_ept_executable(pmap_t pmap, pd_entry_t pde)
+{
+
+ if (pmap->pm_type != PT_EPT)
+ return (false);
+ return ((pde & EPT_PG_EXECUTE) != 0);
+}
+
/*
* Tries to promote the 512, contiguous 4KB page mappings that are within a
* single page table page (PTP) to a single 2MB page mapping. For promotion
@@ -4857,7 +4914,9 @@ pmap_promote_pde(pmap_t pmap, pd_entry_t *pde, vm_offset_t va,
firstpte = (pt_entry_t *)PHYS_TO_DMAP(*pde & PG_FRAME);
setpde:
newpde = *firstpte;
- if ((newpde & ((PG_FRAME & PDRMASK) | PG_A | PG_V)) != (PG_A | PG_V)) {
+ if ((newpde & ((PG_FRAME & PDRMASK) | PG_A | PG_V)) != (PG_A | PG_V) ||
+ !pmap_allow_2m_x_page(pmap, pmap_pde_ept_executable(pmap,
+ newpde))) {
atomic_add_long(&pmap_pde_p_failures, 1);
CTR2(KTR_PMAP, "pmap_promote_pde: failure for va %#lx"
" in pmap %p", va, pmap);
@@ -5283,6 +5342,12 @@ pmap_enter_pde(pmap_t pmap, vm_offset_t va, pd_entry_t newpde, u_int flags,
PG_V = pmap_valid_bit(pmap);
PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+ if (!pmap_allow_2m_x_page(pmap, pmap_pde_ept_executable(pmap,
+ newpde))) {
+ CTR2(KTR_PMAP, "pmap_enter_pde: 2m x blocked for va %#lx"
+ " in pmap %p", va, pmap);
+ return (KERN_FAILURE);
+ }
if ((pdpg = pmap_allocpde(pmap, va, (flags & PMAP_ENTER_NOSLEEP) != 0 ?
NULL : lockp)) == NULL) {
CTR2(KTR_PMAP, "pmap_enter_pde: failure for va %#lx"
@@ -5412,6 +5477,7 @@ pmap_enter_object(pmap_t pmap, vm_offset_t start, vm_offset_t end,
va = start + ptoa(diff);
if ((va & PDRMASK) == 0 && va + NBPDR <= end &&
m->psind == 1 && pmap_ps_enabled(pmap) &&
+ pmap_allow_2m_x_page(pmap, (prot & VM_PROT_EXECUTE) != 0) &&
pmap_enter_2mpage(pmap, va, m, prot, &lock))
m = &m[NBPDR / PAGE_SIZE - 1];
else
diff --git a/sys/amd64/include/pmap.h b/sys/amd64/include/pmap.h
index 2060d6bb87d5..a833a5a7c1df 100644
--- a/sys/amd64/include/pmap.h
+++ b/sys/amd64/include/pmap.h
@@ -413,6 +413,7 @@ struct thread;
void pmap_activate_boot(pmap_t pmap);
void pmap_activate_sw(struct thread *);
+void pmap_allow_2m_x_ept_recalculate(void);
void pmap_bootstrap(vm_paddr_t *);
int pmap_cache_bits(pmap_t pmap, int mode, boolean_t is_pde);
int pmap_change_attr(vm_offset_t, vm_size_t, int);
diff --git a/sys/dev/cpuctl/cpuctl.c b/sys/dev/cpuctl/cpuctl.c
index 0ccabd2c2220..ad9a010dbe8f 100644
--- a/sys/dev/cpuctl/cpuctl.c
+++ b/sys/dev/cpuctl/cpuctl.c
@@ -50,6 +50,10 @@ __FBSDID("$FreeBSD$");
#include <sys/pmckern.h>
#include <sys/cpuctl.h>
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
#include <machine/cpufunc.h>
#include <machine/md_var.h>
#include <machine/specialreg.h>
@@ -521,6 +525,9 @@ cpuctl_do_eval_cpu_features(int cpu, struct thread *td)
hw_ibrs_recalculate();
restore_cpu(oldcpu, is_bound, td);
hw_ssb_recalculate(true);
+#ifdef __amd64__
+ pmap_allow_2m_x_ept_recalculate();
+#endif
hw_mds_recalculate();
printcpuinfo();
return (0);
diff --git a/sys/x86/include/specialreg.h b/sys/x86/include/specialreg.h
index ec88deaf68ca..4fd72ea34870 100644
--- a/sys/x86/include/specialreg.h
+++ b/sys/x86/include/specialreg.h
@@ -439,6 +439,7 @@
#define IA32_ARCH_CAP_SKIP_L1DFL_VMENTRY 0x00000008
#define IA32_ARCH_CAP_SSB_NO 0x00000010
#define IA32_ARCH_CAP_MDS_NO 0x00000020
+#define IA32_ARCH_CAP_IF_PSCHANGE_MC_NO 0x00000040
/*
* CPUID manufacturers identifiers