diff options
Diffstat (limited to 'mm/page_table_check.c')
| -rw-r--r-- | mm/page_table_check.c | 30 | 
1 files changed, 30 insertions, 0 deletions
diff --git a/mm/page_table_check.c b/mm/page_table_check.c index af69c3c8f7c2..4169576bed72 100644 --- a/mm/page_table_check.c +++ b/mm/page_table_check.c @@ -7,6 +7,8 @@  #include <linux/kstrtox.h>  #include <linux/mm.h>  #include <linux/page_table_check.h> +#include <linux/swap.h> +#include <linux/swapops.h>  #undef pr_fmt  #define pr_fmt(fmt)	"page_table_check: " fmt @@ -182,6 +184,22 @@ void __page_table_check_pud_clear(struct mm_struct *mm, pud_t pud)  }  EXPORT_SYMBOL(__page_table_check_pud_clear); +/* Whether the swap entry cached writable information */ +static inline bool swap_cached_writable(swp_entry_t entry) +{ +	return is_writable_device_exclusive_entry(entry) || +	    is_writable_device_private_entry(entry) || +	    is_writable_migration_entry(entry); +} + +static inline void page_table_check_pte_flags(pte_t pte) +{ +	if (pte_present(pte) && pte_uffd_wp(pte)) +		WARN_ON_ONCE(pte_write(pte)); +	else if (is_swap_pte(pte) && pte_swp_uffd_wp(pte)) +		WARN_ON_ONCE(swap_cached_writable(pte_to_swp_entry(pte))); +} +  void __page_table_check_ptes_set(struct mm_struct *mm, pte_t *ptep, pte_t pte,  		unsigned int nr)  { @@ -190,6 +208,8 @@ void __page_table_check_ptes_set(struct mm_struct *mm, pte_t *ptep, pte_t pte,  	if (&init_mm == mm)  		return; +	page_table_check_pte_flags(pte); +  	for (i = 0; i < nr; i++)  		__page_table_check_pte_clear(mm, ptep_get(ptep + i));  	if (pte_user_accessible_page(pte)) @@ -197,11 +217,21 @@ void __page_table_check_ptes_set(struct mm_struct *mm, pte_t *ptep, pte_t pte,  }  EXPORT_SYMBOL(__page_table_check_ptes_set); +static inline void page_table_check_pmd_flags(pmd_t pmd) +{ +	if (pmd_present(pmd) && pmd_uffd_wp(pmd)) +		WARN_ON_ONCE(pmd_write(pmd)); +	else if (is_swap_pmd(pmd) && pmd_swp_uffd_wp(pmd)) +		WARN_ON_ONCE(swap_cached_writable(pmd_to_swp_entry(pmd))); +} +  void __page_table_check_pmd_set(struct mm_struct *mm, pmd_t *pmdp, pmd_t pmd)  {  	if (&init_mm == mm)  		return; +	page_table_check_pmd_flags(pmd); +  	__page_table_check_pmd_clear(mm, *pmdp);  	if (pmd_user_accessible_page(pmd)) {  		page_table_check_set(pmd_pfn(pmd), PMD_SIZE >> PAGE_SHIFT,  |