83 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			83 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | |
| From: Andy Lutomirski <luto@kernel.org>
 | |
| Date: Wed, 26 Jul 2017 07:16:30 -0700
 | |
| Subject: [PATCH] x86/ldt/64: Refresh DS and ES when modify_ldt changes an
 | |
|  entry
 | |
| MIME-Version: 1.0
 | |
| Content-Type: text/plain; charset=UTF-8
 | |
| Content-Transfer-Encoding: 8bit
 | |
| 
 | |
| CVE-2017-5754
 | |
| 
 | |
| On x86_32, modify_ldt() implicitly refreshes the cached DS and ES
 | |
| segments because they are refreshed on return to usermode.
 | |
| 
 | |
| On x86_64, they're not refreshed on return to usermode.  To improve
 | |
| determinism and match x86_32's behavior, refresh them when we update
 | |
| the LDT.
 | |
| 
 | |
| This avoids a situation in which the DS points to a descriptor that is
 | |
| changed but the old cached segment persists until the next reschedule.
 | |
| If this happens, then the user-visible state will change
 | |
| nondeterministically some time after modify_ldt() returns, which is
 | |
| unfortunate.
 | |
| 
 | |
| Signed-off-by: Andy Lutomirski <luto@kernel.org>
 | |
| Cc: Borislav Petkov <bpetkov@suse.de>
 | |
| Cc: Chang Seok <chang.seok.bae@intel.com>
 | |
| Cc: Linus Torvalds <torvalds@linux-foundation.org>
 | |
| Cc: Peter Zijlstra <peterz@infradead.org>
 | |
| Cc: Thomas Gleixner <tglx@linutronix.de>
 | |
| Signed-off-by: Ingo Molnar <mingo@kernel.org>
 | |
| (cherry picked from commit a632375764aa25c97b78beb56c71b0ba59d1cf83)
 | |
| Signed-off-by: Andy Whitcroft <apw@canonical.com>
 | |
| Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
 | |
| (cherry picked from commit 295cb0b06150958ec84ee4b8844ef7e389e22c4e)
 | |
| Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
 | |
| ---
 | |
|  arch/x86/kernel/ldt.c | 21 +++++++++++++++++++++
 | |
|  1 file changed, 21 insertions(+)
 | |
| 
 | |
| diff --git a/arch/x86/kernel/ldt.c b/arch/x86/kernel/ldt.c
 | |
| index a870910c8565..f0e64db18ac8 100644
 | |
| --- a/arch/x86/kernel/ldt.c
 | |
| +++ b/arch/x86/kernel/ldt.c
 | |
| @@ -21,6 +21,25 @@
 | |
|  #include <asm/mmu_context.h>
 | |
|  #include <asm/syscalls.h>
 | |
|  
 | |
| +static void refresh_ldt_segments(void)
 | |
| +{
 | |
| +#ifdef CONFIG_X86_64
 | |
| +	unsigned short sel;
 | |
| +
 | |
| +	/*
 | |
| +	 * Make sure that the cached DS and ES descriptors match the updated
 | |
| +	 * LDT.
 | |
| +	 */
 | |
| +	savesegment(ds, sel);
 | |
| +	if ((sel & SEGMENT_TI_MASK) == SEGMENT_LDT)
 | |
| +		loadsegment(ds, sel);
 | |
| +
 | |
| +	savesegment(es, sel);
 | |
| +	if ((sel & SEGMENT_TI_MASK) == SEGMENT_LDT)
 | |
| +		loadsegment(es, sel);
 | |
| +#endif
 | |
| +}
 | |
| +
 | |
|  /* context.lock is held for us, so we don't need any locking. */
 | |
|  static void flush_ldt(void *__mm)
 | |
|  {
 | |
| @@ -32,6 +51,8 @@ static void flush_ldt(void *__mm)
 | |
|  
 | |
|  	pc = &mm->context;
 | |
|  	set_ldt(pc->ldt->entries, pc->ldt->nr_entries);
 | |
| +
 | |
| +	refresh_ldt_segments();
 | |
|  }
 | |
|  
 | |
|  /* The caller must call finalize_ldt_struct on the result. LDT starts zeroed. */
 | |
| -- 
 | |
| 2.14.2
 | |
| 
 | 
