mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	x86/mm, mm/hwpoison: Clear PRESENT bit for kernel 1:1 mappings of poison pages
Speculative processor accesses may reference any memory that has a valid page table entry. While a speculative access won't generate a machine check, it will log the error in a machine check bank. That could cause escalation of a subsequent error since the overflow bit will be then set in the machine check bank status register. Code has to be double-plus-tricky to avoid mentioning the 1:1 virtual address of the page we want to map out otherwise we may trigger the very problem we are trying to avoid. We use a non-canonical address that passes through the usual Linux table walking code to get to the same "pte". Thanks to Dave Hansen for reviewing several iterations of this. Also see: http://marc.info/?l=linux-mm&m=149860136413338&w=2 Signed-off-by: Tony Luck <tony.luck@intel.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andy Lutomirski <luto@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Borislav Petkov <bp@suse.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Dave Hansen <dave.hansen@intel.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: Elliott, Robert (Persistent Memory) <elliott@hpe.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: linux-mm@kvack.org Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/20170816171803.28342-1-tony.luck@intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
		
							parent
							
								
									57bd1905b2
								
							
						
					
					
						commit
						ce0fa3e56a
					
				
					 4 changed files with 55 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -51,6 +51,10 @@ static inline void clear_page(void *page)
 | 
			
		|||
 | 
			
		||||
void copy_page(void *to, void *from);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_X86_MCE
 | 
			
		||||
#define arch_unmap_kpfn arch_unmap_kpfn
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif	/* !__ASSEMBLY__ */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_X86_VSYSCALL_EMULATION
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,7 @@
 | 
			
		|||
#include <asm/mce.h>
 | 
			
		||||
#include <asm/msr.h>
 | 
			
		||||
#include <asm/reboot.h>
 | 
			
		||||
#include <asm/set_memory.h>
 | 
			
		||||
 | 
			
		||||
#include "mce-internal.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1051,6 +1052,48 @@ static int do_memory_failure(struct mce *m)
 | 
			
		|||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined(arch_unmap_kpfn) && defined(CONFIG_MEMORY_FAILURE)
 | 
			
		||||
 | 
			
		||||
void arch_unmap_kpfn(unsigned long pfn)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long decoy_addr;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Unmap this page from the kernel 1:1 mappings to make sure
 | 
			
		||||
	 * we don't log more errors because of speculative access to
 | 
			
		||||
	 * the page.
 | 
			
		||||
	 * We would like to just call:
 | 
			
		||||
	 *	set_memory_np((unsigned long)pfn_to_kaddr(pfn), 1);
 | 
			
		||||
	 * but doing that would radically increase the odds of a
 | 
			
		||||
	 * speculative access to the posion page because we'd have
 | 
			
		||||
	 * the virtual address of the kernel 1:1 mapping sitting
 | 
			
		||||
	 * around in registers.
 | 
			
		||||
	 * Instead we get tricky.  We create a non-canonical address
 | 
			
		||||
	 * that looks just like the one we want, but has bit 63 flipped.
 | 
			
		||||
	 * This relies on set_memory_np() not checking whether we passed
 | 
			
		||||
	 * a legal address.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Build time check to see if we have a spare virtual bit. Don't want
 | 
			
		||||
 * to leave this until run time because most developers don't have a
 | 
			
		||||
 * system that can exercise this code path. This will only become a
 | 
			
		||||
 * problem if/when we move beyond 5-level page tables.
 | 
			
		||||
 *
 | 
			
		||||
 * Hard code "9" here because cpp doesn't grok ilog2(PTRS_PER_PGD)
 | 
			
		||||
 */
 | 
			
		||||
#if PGDIR_SHIFT + 9 < 63
 | 
			
		||||
	decoy_addr = (pfn << PAGE_SHIFT) + (PAGE_OFFSET ^ BIT(63));
 | 
			
		||||
#else
 | 
			
		||||
#error "no unused virtual bit available"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (set_memory_np(decoy_addr, 1))
 | 
			
		||||
		pr_warn("Could not invalidate pfn=0x%lx from 1:1 map\n", pfn);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The actual machine check handler. This only handles real
 | 
			
		||||
 * exceptions when something got corrupted coming in through int 18.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -126,4 +126,10 @@ static __always_inline enum lru_list page_lru(struct page *page)
 | 
			
		|||
 | 
			
		||||
#define lru_to_page(head) (list_entry((head)->prev, struct page, lru))
 | 
			
		||||
 | 
			
		||||
#ifdef arch_unmap_kpfn
 | 
			
		||||
extern void arch_unmap_kpfn(unsigned long pfn);
 | 
			
		||||
#else
 | 
			
		||||
static __always_inline void arch_unmap_kpfn(unsigned long pfn) { }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1146,6 +1146,8 @@ int memory_failure(unsigned long pfn, int trapno, int flags)
 | 
			
		|||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	arch_unmap_kpfn(pfn);
 | 
			
		||||
 | 
			
		||||
	orig_head = hpage = compound_head(p);
 | 
			
		||||
	num_poisoned_pages_inc();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue