diff mbox series

[SRU,BIONIC] mm/vmalloc: Sync unmappings in __purge_vmap_area_lazy()

Message ID 20190728125311.10369-1-colin.king@canonical.com
State New
Headers show
Series [SRU,BIONIC] mm/vmalloc: Sync unmappings in __purge_vmap_area_lazy() | expand

Commit Message

Colin Ian King July 28, 2019, 12:53 p.m. UTC
From: Colin Ian King <colin.king@canonical.com>

BugLink: https://bugs.launchpad.net/bugs/1838115

On x86-32 with PTI enabled, parts of the kernel page-tables are not shared
between processes. This can cause mappings in the vmalloc/ioremap area to
persist in some page-tables after the region is unmapped and released.

When the region is re-used the processes with the old mappings do not fault
in the new mappings but still access the old ones.

This causes undefined behavior, in reality often data corruption, kernel
oopses and panics and even spontaneous reboots.

Fix this problem by activly syncing unmaps in the vmalloc/ioremap area to
all page-tables in the system before the regions can be re-used.

References: https://bugzilla.suse.com/show_bug.cgi?id=1118689
Fixes: 5d72b4fba40ef ('x86, mm: support huge I/O mapping capability I/F')
Signed-off-by: Joerg Roedel <jroedel@suse.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
Link: https://lkml.kernel.org/r/20190719184652.11391-4-joro@8bytes.org
(backported from commit 3f8fd02b1bf1d7ba964485a56f2f4b53ae88c167)
Signed-off-by: Colin Ian King <colin.king@canonical.com>
---
 arch/x86/mm/fault.c | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

Comments

Colin Ian King July 28, 2019, 12:54 p.m. UTC | #1
Sorry, actually DISCO and for DISCO HWE kernel. Apology for confusion.

Colin

On 28/07/2019 13:53, Colin King wrote:
> From: Colin Ian King <colin.king@canonical.com>
> 
> BugLink: https://bugs.launchpad.net/bugs/1838115
> 
> On x86-32 with PTI enabled, parts of the kernel page-tables are not shared
> between processes. This can cause mappings in the vmalloc/ioremap area to
> persist in some page-tables after the region is unmapped and released.
> 
> When the region is re-used the processes with the old mappings do not fault
> in the new mappings but still access the old ones.
> 
> This causes undefined behavior, in reality often data corruption, kernel
> oopses and panics and even spontaneous reboots.
> 
> Fix this problem by activly syncing unmaps in the vmalloc/ioremap area to
> all page-tables in the system before the regions can be re-used.
> 
> References: https://bugzilla.suse.com/show_bug.cgi?id=1118689
> Fixes: 5d72b4fba40ef ('x86, mm: support huge I/O mapping capability I/F')
> Signed-off-by: Joerg Roedel <jroedel@suse.de>
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
> Link: https://lkml.kernel.org/r/20190719184652.11391-4-joro@8bytes.org
> (backported from commit 3f8fd02b1bf1d7ba964485a56f2f4b53ae88c167)
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
> ---
>  arch/x86/mm/fault.c | 13 +++++--------
>  1 file changed, 5 insertions(+), 8 deletions(-)
> 
> diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
> index 9d5c75f02295..eb246b3df03d 100644
> --- a/arch/x86/mm/fault.c
> +++ b/arch/x86/mm/fault.c
> @@ -193,11 +193,12 @@ static inline pmd_t *vmalloc_sync_one(pgd_t *pgd, unsigned long address)
>  
>  	pmd = pmd_offset(pud, address);
>  	pmd_k = pmd_offset(pud_k, address);
> -	if (!pmd_present(*pmd_k))
> -		return NULL;
>  
> -	if (!pmd_present(*pmd))
> +	if (pmd_present(*pmd) != pmd_present(*pmd_k))
>  		set_pmd(pmd, *pmd_k);
> +
> +	if (!pmd_present(*pmd_k))
> +		return NULL;
>  	else
>  		BUG_ON(pmd_page(*pmd) != pmd_page(*pmd_k));
>  
> @@ -219,17 +220,13 @@ void vmalloc_sync_all(void)
>  		spin_lock(&pgd_lock);
>  		list_for_each_entry(page, &pgd_list, lru) {
>  			spinlock_t *pgt_lock;
> -			pmd_t *ret;
>  
>  			/* the pgt_lock only for Xen */
>  			pgt_lock = &pgd_page_get_mm(page)->page_table_lock;
>  
>  			spin_lock(pgt_lock);
> -			ret = vmalloc_sync_one(page_address(page), address);
> +			vmalloc_sync_one(page_address(page), address);
>  			spin_unlock(pgt_lock);
> -
> -			if (!ret)
> -				break;
>  		}
>  		spin_unlock(&pgd_lock);
>  	}
>
Colin Ian King July 28, 2019, 1 p.m. UTC | #2
I bodged this up, V2 coming soon.

On 28/07/2019 13:54, Colin Ian King wrote:
> Sorry, actually DISCO and for DISCO HWE kernel. Apology for confusion.
> 
> Colin
> 
> On 28/07/2019 13:53, Colin King wrote:
>> From: Colin Ian King <colin.king@canonical.com>
>>
>> BugLink: https://bugs.launchpad.net/bugs/1838115
>>
>> On x86-32 with PTI enabled, parts of the kernel page-tables are not shared
>> between processes. This can cause mappings in the vmalloc/ioremap area to
>> persist in some page-tables after the region is unmapped and released.
>>
>> When the region is re-used the processes with the old mappings do not fault
>> in the new mappings but still access the old ones.
>>
>> This causes undefined behavior, in reality often data corruption, kernel
>> oopses and panics and even spontaneous reboots.
>>
>> Fix this problem by activly syncing unmaps in the vmalloc/ioremap area to
>> all page-tables in the system before the regions can be re-used.
>>
>> References: https://bugzilla.suse.com/show_bug.cgi?id=1118689
>> Fixes: 5d72b4fba40ef ('x86, mm: support huge I/O mapping capability I/F')
>> Signed-off-by: Joerg Roedel <jroedel@suse.de>
>> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
>> Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
>> Link: https://lkml.kernel.org/r/20190719184652.11391-4-joro@8bytes.org
>> (backported from commit 3f8fd02b1bf1d7ba964485a56f2f4b53ae88c167)
>> Signed-off-by: Colin Ian King <colin.king@canonical.com>
>> ---
>>  arch/x86/mm/fault.c | 13 +++++--------
>>  1 file changed, 5 insertions(+), 8 deletions(-)
>>
>> diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
>> index 9d5c75f02295..eb246b3df03d 100644
>> --- a/arch/x86/mm/fault.c
>> +++ b/arch/x86/mm/fault.c
>> @@ -193,11 +193,12 @@ static inline pmd_t *vmalloc_sync_one(pgd_t *pgd, unsigned long address)
>>  
>>  	pmd = pmd_offset(pud, address);
>>  	pmd_k = pmd_offset(pud_k, address);
>> -	if (!pmd_present(*pmd_k))
>> -		return NULL;
>>  
>> -	if (!pmd_present(*pmd))
>> +	if (pmd_present(*pmd) != pmd_present(*pmd_k))
>>  		set_pmd(pmd, *pmd_k);
>> +
>> +	if (!pmd_present(*pmd_k))
>> +		return NULL;
>>  	else
>>  		BUG_ON(pmd_page(*pmd) != pmd_page(*pmd_k));
>>  
>> @@ -219,17 +220,13 @@ void vmalloc_sync_all(void)
>>  		spin_lock(&pgd_lock);
>>  		list_for_each_entry(page, &pgd_list, lru) {
>>  			spinlock_t *pgt_lock;
>> -			pmd_t *ret;
>>  
>>  			/* the pgt_lock only for Xen */
>>  			pgt_lock = &pgd_page_get_mm(page)->page_table_lock;
>>  
>>  			spin_lock(pgt_lock);
>> -			ret = vmalloc_sync_one(page_address(page), address);
>> +			vmalloc_sync_one(page_address(page), address);
>>  			spin_unlock(pgt_lock);
>> -
>> -			if (!ret)
>> -				break;
>>  		}
>>  		spin_unlock(&pgd_lock);
>>  	}
>>
> 
>
diff mbox series

Patch

diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 9d5c75f02295..eb246b3df03d 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -193,11 +193,12 @@  static inline pmd_t *vmalloc_sync_one(pgd_t *pgd, unsigned long address)
 
 	pmd = pmd_offset(pud, address);
 	pmd_k = pmd_offset(pud_k, address);
-	if (!pmd_present(*pmd_k))
-		return NULL;
 
-	if (!pmd_present(*pmd))
+	if (pmd_present(*pmd) != pmd_present(*pmd_k))
 		set_pmd(pmd, *pmd_k);
+
+	if (!pmd_present(*pmd_k))
+		return NULL;
 	else
 		BUG_ON(pmd_page(*pmd) != pmd_page(*pmd_k));
 
@@ -219,17 +220,13 @@  void vmalloc_sync_all(void)
 		spin_lock(&pgd_lock);
 		list_for_each_entry(page, &pgd_list, lru) {
 			spinlock_t *pgt_lock;
-			pmd_t *ret;
 
 			/* the pgt_lock only for Xen */
 			pgt_lock = &pgd_page_get_mm(page)->page_table_lock;
 
 			spin_lock(pgt_lock);
-			ret = vmalloc_sync_one(page_address(page), address);
+			vmalloc_sync_one(page_address(page), address);
 			spin_unlock(pgt_lock);
-
-			if (!ret)
-				break;
 		}
 		spin_unlock(&pgd_lock);
 	}