FAILED: patch "[PATCH] x86/ldt: Unmap PTEs for the slot before freeing LDT pages" failed to apply to 4.18-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 4.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
Quoted 1 line
From
a0e6e0831c516860fc7f9be1db6c081fe902ebcfMon Sep 17 00:00:00 2001
From: "Kirill A. Shutemov" <[email protected]>
Date: Fri, 26 Oct 2018 15:28:55 +0300
Subject: [PATCH] x86/ldt: Unmap PTEs for the slot before freeing LDT pages
modify_ldt(2) leaves the old LDT mapped after switching over to the new one. The old LDT gets freed and the pages can be re-used.
Leaving the mapping in place can have security implications. The mapping is present in the userspace page tables and Meltdown-like attacks can read these freed and possibly reused pages.
It's relatively simple to fix: unmap the old LDT and flush TLB before freeing the old LDT memory.
This further allows to avoid flushing the TLB in map_ldt_struct() as the slot is unmapped and flushed by unmap_ldt_struct() or has never been mapped at all.
[ tglx: Massaged changelog and removed the needless line breaks ]
arch/x86/kernel/ldt.c +38 -13
@@ -199,14 +199,6 @@ static void sanity_check_ldt_mapping(struct mm_struct *mm)/* * If PTI is enabled, this maps the LDT into the kernelmode and * usermode tables for the given mm. -* -* There is no corresponding unmap function. Even if the LDT is freed, we -* leave the PTEs around until the slot is reused or the mm is destroyed. -* This is harmless: the LDT is always in ordinary memory, and no one will -* access the freed slot. -* -* If we wanted to unmap freed LDTs, we'd also need to do a flush to make -* it useful, and the flush would slow down modify_ldt(). */ static int map_ldt_struct (struct mm_struct *mm,struct ldt_struct *ldt,int slot)
@@ -214,8 +206,8 @@ map_ldt_struct(struct mm_struct *mm, struct ldt_struct *ldt, int slot)unsigned long va;bool is_vmalloc;spinlock_t *ptl; +int i, nr_pages;pgd_t *pgd; -int i;if (!static_cpu_has (X86_FEATURE_PTI ))return 0 ;
@@ -238,7 +230,9 @@ map_ldt_struct(struct mm_struct *mm, struct ldt_struct *ldt, int slot) is_vmalloc =is_vmalloc_addr (ldt->entries); -for (i =0 ; i *PAGE_SIZE < ldt->nr_entries *LDT_ENTRY_SIZE ; i++) { + nr_pages =DIV_ROUND_UP (ldt->nr_entries *LDT_ENTRY_SIZE ,PAGE_SIZE ); + +for (i =0 ; i < nr_pages; i++) {unsigned long offset = i <<PAGE_SHIFT ;const void *src = (char *)ldt->entries + offset;unsigned long pfn;
@@ -272,13 +266,39 @@ map_ldt_struct(struct mm_struct *mm, struct ldt_struct *ldt, int slot)/* Propagate LDT mapping to the user page-table */ map_ldt_struct_to_user (mm); - va = (unsigned long )ldt_slot_va (slot); -flush_tlb_mm_range (mm, va, va +LDT_SLOT_STRIDE ,PAGE_SHIFT , false); - ldt->slot = slot;return 0 ; } +static void unmap_ldt_struct (struct mm_struct *mm,struct ldt_struct *ldt) +{ +unsigned long va; +int i, nr_pages; + +if (!ldt) +return ; + +/* LDT map/unmap is only required for PTI */ +if (!static_cpu_has (X86_FEATURE_PTI )) +return ; + + nr_pages =DIV_ROUND_UP (ldt->nr_entries *LDT_ENTRY_SIZE ,PAGE_SIZE ); + +for (i =0 ; i < nr_pages; i++) { +unsigned long offset = i <<PAGE_SHIFT ; +spinlock_t *ptl; +pte_t *ptep; + + va = (unsigned long )ldt_slot_va (ldt->slot) + offset; + ptep =get_locked_pte (mm, va, &ptl); +pte_clear (mm, va, ptep); +pte_unmap_unlock (ptep, ptl); + } + + va = (unsigned long )ldt_slot_va (ldt->slot); +flush_tlb_mm_range (mm, va, va + nr_pages *PAGE_SIZE ,PAGE_SHIFT , false); +} +#else /* !CONFIG_PAGE_TABLE_ISOLATION */ static int
@@ -286,6 +306,10 @@ map_ldt_struct(struct mm_struct *mm, struct ldt_struct *ldt, int slot) {return 0 ; } + +static void unmap_ldt_struct (struct mm_struct *mm,struct ldt_struct *ldt) +{ +}#endif /* CONFIG_PAGE_TABLE_ISOLATION */ static void free_ldt_pgtables (struct mm_struct *mm)
@@ -524,6 +548,7 @@ static int write_ldt(void __user *ptr, unsigned long bytecount, int oldmode) }install_ldt (mm, new_ldt); +unmap_ldt_struct (mm, old_ldt);free_ldt_struct (old_ldt); error =0 ;
Quoted 7 lines · On Mon, Nov 19, 2018 at 12:24:33PM +0000, [email protected] wrote:
The patch below does not apply to the 4.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.From
13e702f756c72dc7c7f2d16920f3b31c9a154f8cMon Sep 17 00:00:00 2001
From: "Kirill A. Shutemov" <[email protected]>
Date: Fri, 26 Oct 2018 15:28:55 +0300
Subject: [PATCH 2/2] x86/ldt: Unmap PTEs for the slot before freeing LDT pages
commit a0e6e0831c516860fc7f9be1db6c081fe902ebcf upstream
modify_ldt(2) leaves the old LDT mapped after switching over to the new one. The old LDT gets freed and the pages can be re-used.
Leaving the mapping in place can have security implications. The mapping is present in the userspace page tables and Meltdown-like attacks can read these freed and possibly reused pages.
It's relatively simple to fix: unmap the old LDT and flush TLB before freeing the old LDT memory.
This further allows to avoid flushing the TLB in map_ldt_struct() as the slot is unmapped and flushed by unmap_ldt_struct() or has never been mapped at all.
[ tglx: Massaged changelog and removed the needless line breaks ]
--- arch/x86/kernel/ldt.c | 49 +++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 13 deletions(-)
arch/x86/kernel/ldt.c +36 -13
@@ -103,14 +103,6 @@ static struct ldt_struct *alloc_ldt_struct(unsigned int num_entries)/* * If PTI is enabled, this maps the LDT into the kernelmode and * usermode tables for the given mm. -* -* There is no corresponding unmap function. Even if the LDT is freed, we -* leave the PTEs around until the slot is reused or the mm is destroyed. -* This is harmless: the LDT is always in ordinary memory, and no one will -* access the freed slot. -* -* If we wanted to unmap freed LDTs, we'd also need to do a flush to make -* it useful, and the flush would slow down modify_ldt(). */ static int map_ldt_struct (struct mm_struct *mm,struct ldt_struct *ldt,int slot)
@@ -119,8 +111,8 @@ map_ldt_struct(struct mm_struct *mm, struct ldt_struct *ldt, int slot)bool is_vmalloc, had_top_level_entry;unsigned long va;spinlock_t *ptl; +int i, nr_pages;pgd_t *pgd; -int i;if (!static_cpu_has (X86_FEATURE_PTI ))return 0 ;
@@ -141,7 +133,9 @@ map_ldt_struct(struct mm_struct *mm, struct ldt_struct *ldt, int slot) is_vmalloc =is_vmalloc_addr (ldt->entries); -for (i =0 ; i *PAGE_SIZE < ldt->nr_entries *LDT_ENTRY_SIZE ; i++) { + nr_pages =DIV_ROUND_UP (ldt->nr_entries *LDT_ENTRY_SIZE ,PAGE_SIZE ); + +for (i =0 ; i < nr_pages; i++) {unsigned long offset = i <<PAGE_SHIFT ;const void *src = (char *)ldt->entries + offset;unsigned long pfn;
@@ -193,14 +187,42 @@ map_ldt_struct(struct mm_struct *mm, struct ldt_struct *ldt, int slot) } } - va = (unsigned long )ldt_slot_va (slot); -flush_tlb_mm_range (mm, va, va +LDT_SLOT_STRIDE ,0 ); - ldt->slot = slot;#endif return 0 ; } +static void unmap_ldt_struct (struct mm_struct *mm,struct ldt_struct *ldt) +{ +#ifdef CONFIG_PAGE_TABLE_ISOLATION +unsigned long va; +int i, nr_pages; + +if (!ldt) +return ; + +/* LDT map/unmap is only required for PTI */ +if (!static_cpu_has (X86_FEATURE_PTI )) +return ; + + nr_pages =DIV_ROUND_UP (ldt->nr_entries *LDT_ENTRY_SIZE ,PAGE_SIZE ); + +for (i =0 ; i < nr_pages; i++) { +unsigned long offset = i <<PAGE_SHIFT ; +spinlock_t *ptl; +pte_t *ptep; + + va = (unsigned long )ldt_slot_va (ldt->slot) + offset; + ptep =get_locked_pte (mm, va, &ptl); +pte_clear (mm, va, ptep); +pte_unmap_unlock (ptep, ptl); + } + + va = (unsigned long )ldt_slot_va (ldt->slot); +flush_tlb_mm_range (mm, va, va + nr_pages *PAGE_SIZE ,0 ); +#endif /* CONFIG_PAGE_TABLE_ISOLATION */ +} +static void free_ldt_pgtables (struct mm_struct *mm) {#ifdef CONFIG_PAGE_TABLE_ISOLATION
@@ -437,6 +459,7 @@ static int write_ldt(void __user *ptr, unsigned long bytecount, int oldmode) }install_ldt (mm, new_ldt); +unmap_ldt_struct (mm, old_ldt);free_ldt_struct (old_ldt); error =0 ;
Signature · 1 line
Kirill A. Shutemov
Quoted 30 lines · Kirill A. Shutemov
Quoted 7 lines · On Mon, Nov 19, 2018 at 12:24:33PM +0000, [email protected] wrote:
The patch below does not apply to the 4.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.From
13e702f756c72dc7c7f2d16920f3b31c9a154f8cMon Sep 17 00:00:00 2001From: "Kirill A. Shutemov" <[email protected]>
Date: Fri, 26 Oct 2018 15:28:55 +0300
Subject: [PATCH 2/2] x86/ldt: Unmap PTEs for the slot before freeing LDT pagescommit
a0e6e0831c516860fc7f9be1db6c081fe902ebcfupstream
modify_ldt(2)leaves the old LDT mapped after switching over to the new one. The old LDT gets freed and the pages can be re-used.Leaving the mapping in place can have security implications. The mapping is present in the userspace page tables and Meltdown-like attacks can read these freed and possibly reused pages.
It's relatively simple to fix: unmap the old LDT and flush TLB before freeing the old LDT memory.
This further allows to avoid flushing the TLB in
map_ldt_struct()as the slot is unmapped and flushed byunmap_ldt_struct()or has never been mapped at all.[ tglx: Massaged changelog and removed the needless line breaks ]
Quoted 15 lines
Fixes f55f0501cbf6 ("x86/pti: Put the LDT in its own PGD if PTI is on")Signed-off-by Kirill A. Shutemov <[email protected]>Signed-off-by Thomas Gleixner <[email protected]>
Queued for 4.18, thank you.
--
Thanks,
Sasha