Skip to content

Commit 70442fc

Browse files
committed
Merge tag 'x86_mm_for_v6.1_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 mm updates from Dave Hansen: "There are some small things here, plus one big one. The big one detected and refused to create W+X kernel mappings. This caused a bit of trouble and it is entirely disabled on 32-bit due to known unfixable EFI issues. It also oopsed on some systemd eBPF use, which kept some users from booting. The eBPF issue is fixed, but those troubles were caught relatively recently which made me nervous that there are more lurking. The final commit in here retains the warnings, but doesn't actually refuse to create W+X mappings. Summary: - Detect insecure W+X mappings and warn about them, including a few bug fixes and relaxing the enforcement - Do a long-overdue defconfig update and enabling W+X boot-time detection - Cleanup _PAGE_PSE handling (follow-up on an earlier bug) - Rename a change_page_attr function" * tag 'x86_mm_for_v6.1_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/mm: Ease W^X enforcement back to just a warning x86/mm: Disable W^X detection and enforcement on 32-bit x86/mm: Add prot_sethuge() helper to abstract out _PAGE_PSE handling x86/mm/32: Fix W^X detection when page tables do not support NX x86/defconfig: Enable CONFIG_DEBUG_WX=y x86/defconfig: Refresh the defconfigs x86/mm: Refuse W^X violations x86/mm: Rename set_memory_present() to set_memory_p()
2 parents e230253 + c5129ec commit 70442fc

File tree

4 files changed

+64
-15
lines changed

4 files changed

+64
-15
lines changed

arch/x86/configs/i386_defconfig

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ CONFIG_CGROUP_MISC=y
2727
CONFIG_CGROUP_DEBUG=y
2828
CONFIG_BLK_DEV_INITRD=y
2929
CONFIG_KALLSYMS_ALL=y
30-
# CONFIG_COMPAT_BRK is not set
3130
CONFIG_PROFILING=y
3231
CONFIG_SMP=y
3332
CONFIG_HYPERVISOR_GUEST=y
@@ -44,6 +43,7 @@ CONFIG_EFI_STUB=y
4443
CONFIG_HZ_1000=y
4544
CONFIG_KEXEC=y
4645
CONFIG_CRASH_DUMP=y
46+
# CONFIG_RETHUNK is not set
4747
CONFIG_HIBERNATION=y
4848
CONFIG_PM_DEBUG=y
4949
CONFIG_PM_TRACE_RTC=y
@@ -62,6 +62,7 @@ CONFIG_BLK_CGROUP_IOLATENCY=y
6262
CONFIG_BLK_CGROUP_IOCOST=y
6363
CONFIG_BLK_CGROUP_IOPRIO=y
6464
CONFIG_BINFMT_MISC=y
65+
# CONFIG_COMPAT_BRK is not set
6566
CONFIG_NET=y
6667
CONFIG_PACKET=y
6768
CONFIG_UNIX=y
@@ -269,9 +270,10 @@ CONFIG_SECURITY_SELINUX=y
269270
CONFIG_SECURITY_SELINUX_BOOTPARAM=y
270271
CONFIG_SECURITY_SELINUX_DISABLE=y
271272
CONFIG_PRINTK_TIME=y
273+
CONFIG_DEBUG_KERNEL=y
272274
CONFIG_FRAME_WARN=1024
273275
CONFIG_MAGIC_SYSRQ=y
274-
CONFIG_DEBUG_KERNEL=y
276+
CONFIG_DEBUG_WX=y
275277
CONFIG_DEBUG_STACK_USAGE=y
276278
# CONFIG_SCHED_DEBUG is not set
277279
CONFIG_SCHEDSTATS=y

arch/x86/configs/x86_64_defconfig

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ CONFIG_CGROUP_MISC=y
2626
CONFIG_CGROUP_DEBUG=y
2727
CONFIG_BLK_DEV_INITRD=y
2828
CONFIG_KALLSYMS_ALL=y
29-
# CONFIG_COMPAT_BRK is not set
3029
CONFIG_PROFILING=y
3130
CONFIG_SMP=y
3231
CONFIG_HYPERVISOR_GUEST=y
@@ -62,6 +61,7 @@ CONFIG_BLK_CGROUP_IOLATENCY=y
6261
CONFIG_BLK_CGROUP_IOCOST=y
6362
CONFIG_BLK_CGROUP_IOPRIO=y
6463
CONFIG_BINFMT_MISC=y
64+
# CONFIG_COMPAT_BRK is not set
6565
CONFIG_NET=y
6666
CONFIG_PACKET=y
6767
CONFIG_UNIX=y
@@ -267,8 +267,9 @@ CONFIG_SECURITY_SELINUX=y
267267
CONFIG_SECURITY_SELINUX_BOOTPARAM=y
268268
CONFIG_SECURITY_SELINUX_DISABLE=y
269269
CONFIG_PRINTK_TIME=y
270-
CONFIG_MAGIC_SYSRQ=y
271270
CONFIG_DEBUG_KERNEL=y
271+
CONFIG_MAGIC_SYSRQ=y
272+
CONFIG_DEBUG_WX=y
272273
CONFIG_DEBUG_STACK_USAGE=y
273274
# CONFIG_SCHED_DEBUG is not set
274275
CONFIG_SCHEDSTATS=y

arch/x86/mm/init_64.c

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ DEFINE_ENTRY(pud, pud, init)
9090
DEFINE_ENTRY(pmd, pmd, init)
9191
DEFINE_ENTRY(pte, pte, init)
9292

93+
static inline pgprot_t prot_sethuge(pgprot_t prot)
94+
{
95+
WARN_ON_ONCE(pgprot_val(prot) & _PAGE_PAT);
96+
97+
return __pgprot(pgprot_val(prot) | _PAGE_PSE);
98+
}
9399

94100
/*
95101
* NOTE: pagetable_init alloc all the fixmap pagetables contiguous on the
@@ -557,9 +563,8 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long paddr, unsigned long paddr_end,
557563
if (page_size_mask & (1<<PG_LEVEL_2M)) {
558564
pages++;
559565
spin_lock(&init_mm.page_table_lock);
560-
set_pte_init((pte_t *)pmd,
561-
pfn_pte((paddr & PMD_MASK) >> PAGE_SHIFT,
562-
__pgprot(pgprot_val(prot) | _PAGE_PSE)),
566+
set_pmd_init(pmd,
567+
pfn_pmd(paddr >> PAGE_SHIFT, prot_sethuge(prot)),
563568
init);
564569
spin_unlock(&init_mm.page_table_lock);
565570
paddr_last = paddr_next;
@@ -644,12 +649,8 @@ phys_pud_init(pud_t *pud_page, unsigned long paddr, unsigned long paddr_end,
644649
if (page_size_mask & (1<<PG_LEVEL_1G)) {
645650
pages++;
646651
spin_lock(&init_mm.page_table_lock);
647-
648-
prot = __pgprot(pgprot_val(prot) | _PAGE_PSE);
649-
650-
set_pte_init((pte_t *)pud,
651-
pfn_pte((paddr & PUD_MASK) >> PAGE_SHIFT,
652-
prot),
652+
set_pud_init(pud,
653+
pfn_pud(paddr >> PAGE_SHIFT, prot_sethuge(prot)),
653654
init);
654655
spin_unlock(&init_mm.page_table_lock);
655656
paddr_last = paddr_next;

arch/x86/mm/pat/set_memory.c

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,46 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long start,
579579
return __pgprot(pgprot_val(prot) & ~forbidden);
580580
}
581581

582+
/*
583+
* Validate strict W^X semantics.
584+
*/
585+
static inline pgprot_t verify_rwx(pgprot_t old, pgprot_t new, unsigned long start,
586+
unsigned long pfn, unsigned long npg)
587+
{
588+
unsigned long end;
589+
590+
/*
591+
* 32-bit has some unfixable W+X issues, like EFI code
592+
* and writeable data being in the same page. Disable
593+
* detection and enforcement there.
594+
*/
595+
if (IS_ENABLED(CONFIG_X86_32))
596+
return new;
597+
598+
/* Only verify when NX is supported: */
599+
if (!(__supported_pte_mask & _PAGE_NX))
600+
return new;
601+
602+
if (!((pgprot_val(old) ^ pgprot_val(new)) & (_PAGE_RW | _PAGE_NX)))
603+
return new;
604+
605+
if ((pgprot_val(new) & (_PAGE_RW | _PAGE_NX)) != _PAGE_RW)
606+
return new;
607+
608+
end = start + npg * PAGE_SIZE - 1;
609+
WARN_ONCE(1, "CPA detected W^X violation: %016llx -> %016llx range: 0x%016lx - 0x%016lx PFN %lx\n",
610+
(unsigned long long)pgprot_val(old),
611+
(unsigned long long)pgprot_val(new),
612+
start, end, pfn);
613+
614+
/*
615+
* For now, allow all permission change attempts by returning the
616+
* attempted permissions. This can 'return old' to actively
617+
* refuse the permission change at a later time.
618+
*/
619+
return new;
620+
}
621+
582622
/*
583623
* Lookup the page table entry for a virtual address in a specific pgd.
584624
* Return a pointer to the entry and the level of the mapping.
@@ -885,6 +925,8 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address,
885925
new_prot = static_protections(req_prot, lpaddr, old_pfn, numpages,
886926
psize, CPA_DETECT);
887927

928+
new_prot = verify_rwx(old_prot, new_prot, lpaddr, old_pfn, numpages);
929+
888930
/*
889931
* If there is a conflict, split the large page.
890932
*
@@ -1525,6 +1567,7 @@ static int __change_page_attr(struct cpa_data *cpa, int primary)
15251567

15261568
if (level == PG_LEVEL_4K) {
15271569
pte_t new_pte;
1570+
pgprot_t old_prot = pte_pgprot(old_pte);
15281571
pgprot_t new_prot = pte_pgprot(old_pte);
15291572
unsigned long pfn = pte_pfn(old_pte);
15301573

@@ -1536,6 +1579,8 @@ static int __change_page_attr(struct cpa_data *cpa, int primary)
15361579
new_prot = static_protections(new_prot, address, pfn, 1, 0,
15371580
CPA_PROTECT);
15381581

1582+
new_prot = verify_rwx(old_prot, new_prot, address, pfn, 1);
1583+
15391584
new_prot = pgprot_clear_protnone_bits(new_prot);
15401585

15411586
/*
@@ -1944,7 +1989,7 @@ int set_mce_nospec(unsigned long pfn)
19441989
return rc;
19451990
}
19461991

1947-
static int set_memory_present(unsigned long *addr, int numpages)
1992+
static int set_memory_p(unsigned long *addr, int numpages)
19481993
{
19491994
return change_page_attr_set(addr, numpages, __pgprot(_PAGE_PRESENT), 0);
19501995
}
@@ -1954,7 +1999,7 @@ int clear_mce_nospec(unsigned long pfn)
19541999
{
19552000
unsigned long addr = (unsigned long) pfn_to_kaddr(pfn);
19562001

1957-
return set_memory_present(&addr, 1);
2002+
return set_memory_p(&addr, 1);
19582003
}
19592004
EXPORT_SYMBOL_GPL(clear_mce_nospec);
19602005
#endif /* CONFIG_X86_64 */

0 commit comments

Comments
 (0)