Skip to content

Commit eabb629

Browse files
committed
Merge tag 'x86-mm-2024-05-13' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 mm updates from Ingo Molnar: - Fix W^X violation check false-positives in the CPA code when running as a Xen PV guest - Fix W^X violation warning false-positives in show_fault_oops() * tag 'x86-mm-2024-05-13' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/pat: Fix W^X violation false-positives when running as Xen PV guest x86/pat: Restructure _lookup_address_cpa() x86/mm: Use lookup_address_in_pgd_attr() in show_fault_oops() x86/pat: Introduce lookup_address_in_pgd_attr()
2 parents 963795f + 5bc8b0f commit eabb629

File tree

3 files changed

+60
-17
lines changed

3 files changed

+60
-17
lines changed

arch/x86/include/asm/pgtable_types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,8 @@ static inline void update_page_count(int level, unsigned long pages) { }
567567
extern pte_t *lookup_address(unsigned long address, unsigned int *level);
568568
extern pte_t *lookup_address_in_pgd(pgd_t *pgd, unsigned long address,
569569
unsigned int *level);
570+
pte_t *lookup_address_in_pgd_attr(pgd_t *pgd, unsigned long address,
571+
unsigned int *level, bool *nx, bool *rw);
570572
extern pmd_t *lookup_pmd_address(unsigned long address);
571573
extern phys_addr_t slow_virt_to_phys(void *__address);
572574
extern int __init kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn,

arch/x86/mm/fault.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -514,18 +514,19 @@ show_fault_oops(struct pt_regs *regs, unsigned long error_code, unsigned long ad
514514

515515
if (error_code & X86_PF_INSTR) {
516516
unsigned int level;
517+
bool nx, rw;
517518
pgd_t *pgd;
518519
pte_t *pte;
519520

520521
pgd = __va(read_cr3_pa());
521522
pgd += pgd_index(address);
522523

523-
pte = lookup_address_in_pgd(pgd, address, &level);
524+
pte = lookup_address_in_pgd_attr(pgd, address, &level, &nx, &rw);
524525

525-
if (pte && pte_present(*pte) && !pte_exec(*pte))
526+
if (pte && pte_present(*pte) && (!pte_exec(*pte) || nx))
526527
pr_crit("kernel tried to execute NX-protected page - exploit attempt? (uid: %d)\n",
527528
from_kuid(&init_user_ns, current_uid()));
528-
if (pte && pte_present(*pte) && pte_exec(*pte) &&
529+
if (pte && pte_present(*pte) && pte_exec(*pte) && !nx &&
529530
(pgd_flags(*pgd) & _PAGE_USER) &&
530531
(__read_cr4() & X86_CR4_SMEP))
531532
pr_crit("unable to execute userspace code (SMEP?) (uid: %d)\n",

arch/x86/mm/pat/set_memory.c

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,8 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long start,
619619
* Validate strict W^X semantics.
620620
*/
621621
static inline pgprot_t verify_rwx(pgprot_t old, pgprot_t new, unsigned long start,
622-
unsigned long pfn, unsigned long npg)
622+
unsigned long pfn, unsigned long npg,
623+
bool nx, bool rw)
623624
{
624625
unsigned long end;
625626

@@ -641,6 +642,10 @@ static inline pgprot_t verify_rwx(pgprot_t old, pgprot_t new, unsigned long star
641642
if ((pgprot_val(new) & (_PAGE_RW | _PAGE_NX)) != _PAGE_RW)
642643
return new;
643644

645+
/* Non-leaf translation entries can disable writing or execution. */
646+
if (!rw || nx)
647+
return new;
648+
644649
end = start + npg * PAGE_SIZE - 1;
645650
WARN_ONCE(1, "CPA detected W^X violation: %016llx -> %016llx range: 0x%016lx - 0x%016lx PFN %lx\n",
646651
(unsigned long long)pgprot_val(old),
@@ -657,20 +662,26 @@ static inline pgprot_t verify_rwx(pgprot_t old, pgprot_t new, unsigned long star
657662

658663
/*
659664
* Lookup the page table entry for a virtual address in a specific pgd.
660-
* Return a pointer to the entry and the level of the mapping.
665+
* Return a pointer to the entry, the level of the mapping, and the effective
666+
* NX and RW bits of all page table levels.
661667
*/
662-
pte_t *lookup_address_in_pgd(pgd_t *pgd, unsigned long address,
663-
unsigned int *level)
668+
pte_t *lookup_address_in_pgd_attr(pgd_t *pgd, unsigned long address,
669+
unsigned int *level, bool *nx, bool *rw)
664670
{
665671
p4d_t *p4d;
666672
pud_t *pud;
667673
pmd_t *pmd;
668674

669675
*level = PG_LEVEL_NONE;
676+
*nx = false;
677+
*rw = true;
670678

671679
if (pgd_none(*pgd))
672680
return NULL;
673681

682+
*nx |= pgd_flags(*pgd) & _PAGE_NX;
683+
*rw &= pgd_flags(*pgd) & _PAGE_RW;
684+
674685
p4d = p4d_offset(pgd, address);
675686
if (p4d_none(*p4d))
676687
return NULL;
@@ -679,6 +690,9 @@ pte_t *lookup_address_in_pgd(pgd_t *pgd, unsigned long address,
679690
if (p4d_leaf(*p4d) || !p4d_present(*p4d))
680691
return (pte_t *)p4d;
681692

693+
*nx |= p4d_flags(*p4d) & _PAGE_NX;
694+
*rw &= p4d_flags(*p4d) & _PAGE_RW;
695+
682696
pud = pud_offset(p4d, address);
683697
if (pud_none(*pud))
684698
return NULL;
@@ -687,6 +701,9 @@ pte_t *lookup_address_in_pgd(pgd_t *pgd, unsigned long address,
687701
if (pud_leaf(*pud) || !pud_present(*pud))
688702
return (pte_t *)pud;
689703

704+
*nx |= pud_flags(*pud) & _PAGE_NX;
705+
*rw &= pud_flags(*pud) & _PAGE_RW;
706+
690707
pmd = pmd_offset(pud, address);
691708
if (pmd_none(*pmd))
692709
return NULL;
@@ -695,11 +712,26 @@ pte_t *lookup_address_in_pgd(pgd_t *pgd, unsigned long address,
695712
if (pmd_leaf(*pmd) || !pmd_present(*pmd))
696713
return (pte_t *)pmd;
697714

715+
*nx |= pmd_flags(*pmd) & _PAGE_NX;
716+
*rw &= pmd_flags(*pmd) & _PAGE_RW;
717+
698718
*level = PG_LEVEL_4K;
699719

700720
return pte_offset_kernel(pmd, address);
701721
}
702722

723+
/*
724+
* Lookup the page table entry for a virtual address in a specific pgd.
725+
* Return a pointer to the entry and the level of the mapping.
726+
*/
727+
pte_t *lookup_address_in_pgd(pgd_t *pgd, unsigned long address,
728+
unsigned int *level)
729+
{
730+
bool nx, rw;
731+
732+
return lookup_address_in_pgd_attr(pgd, address, level, &nx, &rw);
733+
}
734+
703735
/*
704736
* Lookup the page table entry for a virtual address. Return a pointer
705737
* to the entry and the level of the mapping.
@@ -715,13 +747,16 @@ pte_t *lookup_address(unsigned long address, unsigned int *level)
715747
EXPORT_SYMBOL_GPL(lookup_address);
716748

717749
static pte_t *_lookup_address_cpa(struct cpa_data *cpa, unsigned long address,
718-
unsigned int *level)
750+
unsigned int *level, bool *nx, bool *rw)
719751
{
720-
if (cpa->pgd)
721-
return lookup_address_in_pgd(cpa->pgd + pgd_index(address),
722-
address, level);
752+
pgd_t *pgd;
753+
754+
if (!cpa->pgd)
755+
pgd = pgd_offset_k(address);
756+
else
757+
pgd = cpa->pgd + pgd_index(address);
723758

724-
return lookup_address(address, level);
759+
return lookup_address_in_pgd_attr(pgd, address, level, nx, rw);
725760
}
726761

727762
/*
@@ -849,12 +884,13 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address,
849884
pgprot_t old_prot, new_prot, req_prot, chk_prot;
850885
pte_t new_pte, *tmp;
851886
enum pg_level level;
887+
bool nx, rw;
852888

853889
/*
854890
* Check for races, another CPU might have split this page
855891
* up already:
856892
*/
857-
tmp = _lookup_address_cpa(cpa, address, &level);
893+
tmp = _lookup_address_cpa(cpa, address, &level, &nx, &rw);
858894
if (tmp != kpte)
859895
return 1;
860896

@@ -965,7 +1001,8 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address,
9651001
new_prot = static_protections(req_prot, lpaddr, old_pfn, numpages,
9661002
psize, CPA_DETECT);
9671003

968-
new_prot = verify_rwx(old_prot, new_prot, lpaddr, old_pfn, numpages);
1004+
new_prot = verify_rwx(old_prot, new_prot, lpaddr, old_pfn, numpages,
1005+
nx, rw);
9691006

9701007
/*
9711008
* If there is a conflict, split the large page.
@@ -1046,14 +1083,15 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
10461083
pte_t *pbase = (pte_t *)page_address(base);
10471084
unsigned int i, level;
10481085
pgprot_t ref_prot;
1086+
bool nx, rw;
10491087
pte_t *tmp;
10501088

10511089
spin_lock(&pgd_lock);
10521090
/*
10531091
* Check for races, another CPU might have split this page
10541092
* up for us already:
10551093
*/
1056-
tmp = _lookup_address_cpa(cpa, address, &level);
1094+
tmp = _lookup_address_cpa(cpa, address, &level, &nx, &rw);
10571095
if (tmp != kpte) {
10581096
spin_unlock(&pgd_lock);
10591097
return 1;
@@ -1594,10 +1632,11 @@ static int __change_page_attr(struct cpa_data *cpa, int primary)
15941632
int do_split, err;
15951633
unsigned int level;
15961634
pte_t *kpte, old_pte;
1635+
bool nx, rw;
15971636

15981637
address = __cpa_addr(cpa, cpa->curpage);
15991638
repeat:
1600-
kpte = _lookup_address_cpa(cpa, address, &level);
1639+
kpte = _lookup_address_cpa(cpa, address, &level, &nx, &rw);
16011640
if (!kpte)
16021641
return __cpa_process_fault(cpa, address, primary);
16031642

@@ -1619,7 +1658,8 @@ static int __change_page_attr(struct cpa_data *cpa, int primary)
16191658
new_prot = static_protections(new_prot, address, pfn, 1, 0,
16201659
CPA_PROTECT);
16211660

1622-
new_prot = verify_rwx(old_prot, new_prot, address, pfn, 1);
1661+
new_prot = verify_rwx(old_prot, new_prot, address, pfn, 1,
1662+
nx, rw);
16231663

16241664
new_prot = pgprot_clear_protnone_bits(new_prot);
16251665

0 commit comments

Comments
 (0)