Message ID | 20220317141203.3646253-4-tongtiangen@huawei.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | mm: page_table_check: add support on arm64 and riscv | expand |
On Thu, Mar 17, 2022 at 02:12:02PM +0000, Tong Tiangen wrote: > @@ -628,6 +647,25 @@ static inline unsigned long pmd_page_vaddr(pmd_t pmd) > #define pud_leaf(pud) pud_sect(pud) > #define pud_valid(pud) pte_valid(pud_pte(pud)) > > +#ifdef CONFIG_PAGE_TABLE_CHECK > +static inline bool pte_user_accessible_page(pte_t pte) > +{ > + return (pte_val(pte) & PTE_VALID) && (pte_val(pte) & PTE_USER); > +} There is another class of user mappings, execute-only, that have both PTE_USER and PTE_UXN cleared. So this logic should be: pte_valid(pte) && (pte_user(pte) || pte_user_exec(pte)) with pte_user() as: #define pte_user(pte) (!!(pte_val(pte) & PTE_USER)) Do we care about PROT_NONE mappings here? They have the valid bit cleared but pte_present() is true. > +static inline bool pmd_user_accessible_page(pmd_t pmd) > +{ > + return pmd_leaf(pmd) && (pmd_val(pmd) & PTE_VALID) && > + (pmd_val(pmd) & PTE_USER); > +} pmd_leaf() implies valid, so you can skip it if that's the aim. Similar comment to the pte variant on execute-only and PROT_NONE mappings.
在 2022/3/18 3:00, Catalin Marinas 写道: > On Thu, Mar 17, 2022 at 02:12:02PM +0000, Tong Tiangen wrote: >> @@ -628,6 +647,25 @@ static inline unsigned long pmd_page_vaddr(pmd_t pmd) >> #define pud_leaf(pud) pud_sect(pud) >> #define pud_valid(pud) pte_valid(pud_pte(pud)) >> >> +#ifdef CONFIG_PAGE_TABLE_CHECK >> +static inline bool pte_user_accessible_page(pte_t pte) >> +{ >> + return (pte_val(pte) & PTE_VALID) && (pte_val(pte) & PTE_USER); >> +} > > There is another class of user mappings, execute-only, that have both > PTE_USER and PTE_UXN cleared. So this logic should be: > > pte_valid(pte) && (pte_user(pte) || pte_user_exec(pte)) > > with pte_user() as: > > #define pte_user(pte) (!!(pte_val(pte) & PTE_USER)) Good suggestion, the PTC(page table check) can cover UXN page and pte_user(pte) helper is required. > > Do we care about PROT_NONE mappings here? They have the valid bit > cleared but pte_present() is true. > PTC will not check this special type(PROT_NONE) of page. >> +static inline bool pmd_user_accessible_page(pmd_t pmd) >> +{ >> + return pmd_leaf(pmd) && (pmd_val(pmd) & PTE_VALID) && >> + (pmd_val(pmd) & PTE_USER); >> +} > > pmd_leaf() implies valid, so you can skip it if that's the aim. PTC only checks whether the memory block corresponding to the pmd_leaf type can access, for !pmd_leaf, PTC checks at the pte level. So i think this is necessary. > > Similar comment to the pte variant on execute-only and PROT_NONE > mappings Same considerations as above. Thanks. Tong >
On Fri, Mar 18, 2022 at 11:58:22AM +0800, Tong Tiangen wrote: > 在 2022/3/18 3:00, Catalin Marinas 写道: > > On Thu, Mar 17, 2022 at 02:12:02PM +0000, Tong Tiangen wrote: > > > @@ -628,6 +647,25 @@ static inline unsigned long pmd_page_vaddr(pmd_t pmd) > > > #define pud_leaf(pud) pud_sect(pud) > > > #define pud_valid(pud) pte_valid(pud_pte(pud)) > > > +#ifdef CONFIG_PAGE_TABLE_CHECK > > > +static inline bool pte_user_accessible_page(pte_t pte) > > > +{ > > > + return (pte_val(pte) & PTE_VALID) && (pte_val(pte) & PTE_USER); > > > +} [...] > > Do we care about PROT_NONE mappings here? They have the valid bit > > cleared but pte_present() is true. > > > > PTC will not check this special type(PROT_NONE) of page. PROT_NONE is just a permission but since we don't have independent read and write bits in the pte, we implement it as an invalid pte (bit 0 cleared). The other content of the pte is fine, so pte_pfn() should still work. PTC could as well check this, I don't think it hurts. > > > +static inline bool pmd_user_accessible_page(pmd_t pmd) > > > +{ > > > + return pmd_leaf(pmd) && (pmd_val(pmd) & PTE_VALID) && > > > + (pmd_val(pmd) & PTE_USER); > > > +} > > > > pmd_leaf() implies valid, so you can skip it if that's the aim. > > PTC only checks whether the memory block corresponding to the pmd_leaf type > can access, for !pmd_leaf, PTC checks at the pte level. So i think this is > necessary. My point is that the (pmd_val(pmd) & PTE_VALID) check is superfluous since that's covered by pmd_leaf() already.
在 2022/3/19 1:18, Catalin Marinas 写道: > On Fri, Mar 18, 2022 at 11:58:22AM +0800, Tong Tiangen wrote: >> 在 2022/3/18 3:00, Catalin Marinas 写道: >>> On Thu, Mar 17, 2022 at 02:12:02PM +0000, Tong Tiangen wrote: >>>> @@ -628,6 +647,25 @@ static inline unsigned long pmd_page_vaddr(pmd_t pmd) >>>> #define pud_leaf(pud) pud_sect(pud) >>>> #define pud_valid(pud) pte_valid(pud_pte(pud)) >>>> +#ifdef CONFIG_PAGE_TABLE_CHECK >>>> +static inline bool pte_user_accessible_page(pte_t pte) >>>> +{ >>>> + return (pte_val(pte) & PTE_VALID) && (pte_val(pte) & PTE_USER); >>>> +} > [...] >>> Do we care about PROT_NONE mappings here? They have the valid bit >>> cleared but pte_present() is true. >>> >> >> PTC will not check this special type(PROT_NONE) of page. > > PROT_NONE is just a permission but since we don't have independent read > and write bits in the pte, we implement it as an invalid pte (bit 0 > cleared). The other content of the pte is fine, so pte_pfn() should > still work. PTC could as well check this, I don't think it hurts. You have a point and the logic should be: pte_present(pte) && (pte_user(pte) || pte_user_exec(pte)) > >>>> +static inline bool pmd_user_accessible_page(pmd_t pmd) >>>> +{ >>>> + return pmd_leaf(pmd) && (pmd_val(pmd) & PTE_VALID) && >>>> + (pmd_val(pmd) & PTE_USER); >>>> +} >>> >>> pmd_leaf() implies valid, so you can skip it if that's the aim. >> >> PTC only checks whether the memory block corresponding to the pmd_leaf type >> can access, for !pmd_leaf, PTC checks at the pte level. So i think this is >> necessary. > > My point is that the (pmd_val(pmd) & PTE_VALID) check is superfluous > since that's covered by pmd_leaf() already. Oh,i got it,you're right and these will be fixed in v2. Considering all your suggestions, The final logic should be: +#define pte_user(pte) (!!(pte_val(pte) & PTE_USER)) +#define pmd_user(pmd) pte_user(pmd_pte(pmd)) +#define pmd_user_exec(pmd) pte_user_exec(pmd_pte(pmd)) +#define pud_user(pud) pte_user(pud_pte(pud)) +static inline bool pte_user_accessible_page(pte_t pte) +{ + return pte_present(pte) && (pte_user(pte)|| pte_user_exec(pte)); +} +static inline bool pmd_user_accessible_page(pmd_t pmd) +{ + return pmd_present(pmd) && (pmd_user(pmd)|| pmd_user_exec(pmd)); +} +static inline bool pud_user_accessible_page(pud_t pud) +{ + return pud_present(pud) && pud_user(pud); +} >
On Mon, Mar 21, 2022 at 02:15:36PM +0800, Tong Tiangen wrote: > Considering all your suggestions, The final logic should be: > > +#define pte_user(pte) (!!(pte_val(pte) & PTE_USER)) > > +#define pmd_user(pmd) pte_user(pmd_pte(pmd)) > +#define pmd_user_exec(pmd) pte_user_exec(pmd_pte(pmd)) > > +#define pud_user(pud) pte_user(pud_pte(pud)) > > +static inline bool pte_user_accessible_page(pte_t pte) > +{ > + return pte_present(pte) && (pte_user(pte)|| pte_user_exec(pte)); > +} This is fine. > +static inline bool pmd_user_accessible_page(pmd_t pmd) > +{ > + return pmd_present(pmd) && (pmd_user(pmd)|| pmd_user_exec(pmd)); > +} That's fine as well assuming that the function is only called on the set_pmd_at() path where we know that the pmd would be a block mapping (huge page). I think that's the case from a quick look at the current x86 implementation. > +static inline bool pud_user_accessible_page(pud_t pud) > +{ > + return pud_present(pud) && pud_user(pud); > +} Same here.
在 2022/3/22 0:40, Catalin Marinas 写道: > On Mon, Mar 21, 2022 at 02:15:36PM +0800, Tong Tiangen wrote: >> Considering all your suggestions, The final logic should be: >> >> +#define pte_user(pte) (!!(pte_val(pte) & PTE_USER)) >> >> +#define pmd_user(pmd) pte_user(pmd_pte(pmd)) >> +#define pmd_user_exec(pmd) pte_user_exec(pmd_pte(pmd)) >> >> +#define pud_user(pud) pte_user(pud_pte(pud)) >> >> +static inline bool pte_user_accessible_page(pte_t pte) >> +{ >> + return pte_present(pte) && (pte_user(pte)|| pte_user_exec(pte)); >> +} > > This is fine. > >> +static inline bool pmd_user_accessible_page(pmd_t pmd) >> +{ >> + return pmd_present(pmd) && (pmd_user(pmd)|| pmd_user_exec(pmd)); >> +} > > That's fine as well assuming that the function is only called on the > set_pmd_at() path where we know that the pmd would be a block mapping > (huge page). I think that's the case from a quick look at the current > x86 implementation. Yeah, PTC only check pmd block mapping(huge page) and pud is similar. These code logic will be included in V2. Thanks. > >> +static inline bool pud_user_accessible_page(pud_t pud) >> +{ >> + return pud_present(pud) && pud_user(pud); >> +} > > Same here. >
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 962c84952c98..1880dd0a8a11 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -91,6 +91,7 @@ config ARM64 select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_SUPPORTS_INT128 if CC_HAS_INT128 select ARCH_SUPPORTS_NUMA_BALANCING + select ARCH_SUPPORTS_PAGE_TABLE_CHECK select ARCH_WANT_COMPAT_IPC_PARSE_VERSION if COMPAT select ARCH_WANT_DEFAULT_BPF_JIT select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 94e147e5456c..2ea6b5e268d0 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -33,6 +33,7 @@ #include <linux/mmdebug.h> #include <linux/mm_types.h> #include <linux/sched.h> +#include <linux/page_table_check.h> #ifdef CONFIG_TRANSPARENT_HUGEPAGE #define __HAVE_ARCH_FLUSH_PMD_TLB_RANGE @@ -312,7 +313,7 @@ static inline void __check_racy_pte_update(struct mm_struct *mm, pte_t *ptep, __func__, pte_val(old_pte), pte_val(pte)); } -static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, +static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte) { if (pte_present(pte) && pte_user_exec(pte) && !pte_special(pte)) @@ -343,6 +344,13 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, set_pte(ptep, pte); } +static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pte) +{ + page_table_check_pte_set(mm, addr, ptep, pte); + return __set_pte_at(mm, addr, ptep, pte); +} + /* * Huge pte definitions. */ @@ -485,8 +493,19 @@ static inline pmd_t pmd_mkdevmap(pmd_t pmd) #define pud_pfn(pud) ((__pud_to_phys(pud) & PUD_MASK) >> PAGE_SHIFT) #define pfn_pud(pfn,prot) __pud(__phys_to_pud_val((phys_addr_t)(pfn) << PAGE_SHIFT) | pgprot_val(prot)) -#define set_pmd_at(mm, addr, pmdp, pmd) set_pte_at(mm, addr, (pte_t *)pmdp, pmd_pte(pmd)) -#define set_pud_at(mm, addr, pudp, pud) set_pte_at(mm, addr, (pte_t *)pudp, pud_pte(pud)) +static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr, + pmd_t *pmdp, pmd_t pmd) +{ + page_table_check_pmd_set(mm, addr, pmdp, pmd); + return __set_pte_at(mm, addr, (pte_t *)pmdp, pmd_pte(pmd)); +} + +static inline void set_pud_at(struct mm_struct *mm, unsigned long addr, + pud_t *pudp, pud_t pud) +{ + page_table_check_pud_set(mm, addr, pudp, pud); + return __set_pte_at(mm, addr, (pte_t *)pudp, pud_pte(pud)); +} #define __p4d_to_phys(p4d) __pte_to_phys(p4d_pte(p4d)) #define __phys_to_p4d_val(phys) __phys_to_pte_val(phys) @@ -628,6 +647,25 @@ static inline unsigned long pmd_page_vaddr(pmd_t pmd) #define pud_leaf(pud) pud_sect(pud) #define pud_valid(pud) pte_valid(pud_pte(pud)) +#ifdef CONFIG_PAGE_TABLE_CHECK +static inline bool pte_user_accessible_page(pte_t pte) +{ + return (pte_val(pte) & PTE_VALID) && (pte_val(pte) & PTE_USER); +} + +static inline bool pmd_user_accessible_page(pmd_t pmd) +{ + return pmd_leaf(pmd) && (pmd_val(pmd) & PTE_VALID) && + (pmd_val(pmd) & PTE_USER); +} + +static inline bool pud_user_accessible_page(pud_t pud) +{ + return pud_leaf(pud) && (pud_val(pud) & PTE_VALID) && + (pud_val(pud) & PTE_USER); +} +#endif + static inline void set_pud(pud_t *pudp, pud_t pud) { #ifdef __PAGETABLE_PUD_FOLDED @@ -856,11 +894,21 @@ static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma, } #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ +static inline pte_t __ptep_get_and_clear(struct mm_struct *mm, + unsigned long address, pte_t *ptep) +{ + return __pte(xchg_relaxed(&pte_val(*ptep), 0)); +} + #define __HAVE_ARCH_PTEP_GET_AND_CLEAR static inline pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long address, pte_t *ptep) { - return __pte(xchg_relaxed(&pte_val(*ptep), 0)); + pte_t pte = __ptep_get_and_clear(mm, address, ptep); + + page_table_check_pte_clear(mm, address, pte); + + return pte; } #ifdef CONFIG_TRANSPARENT_HUGEPAGE @@ -868,7 +916,11 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm, static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm, unsigned long address, pmd_t *pmdp) { - return pte_pmd(ptep_get_and_clear(mm, address, (pte_t *)pmdp)); + pmd_t pmd = pte_pmd(__ptep_get_and_clear(mm, address, (pte_t *)pmdp)); + + page_table_check_pmd_clear(mm, address, pmd); + + return pmd; } #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ @@ -902,6 +954,7 @@ static inline void pmdp_set_wrprotect(struct mm_struct *mm, static inline pmd_t pmdp_establish(struct vm_area_struct *vma, unsigned long address, pmd_t *pmdp, pmd_t pmd) { + page_table_check_pmd_set(vma->vm_mm, address, pmdp, pmd); return __pmd(xchg_relaxed(&pmd_val(*pmdp), pmd_val(pmd))); } #endif