Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/ldt/64: Refresh DS and ES when modify_ldt changes an entry

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>

authored by

Andy Lutomirski and committed by
Ingo Molnar
a6323757 81d38719

+21
+21
arch/x86/kernel/ldt.c
··· 21 21 #include <asm/mmu_context.h> 22 22 #include <asm/syscalls.h> 23 23 24 + static void refresh_ldt_segments(void) 25 + { 26 + #ifdef CONFIG_X86_64 27 + unsigned short sel; 28 + 29 + /* 30 + * Make sure that the cached DS and ES descriptors match the updated 31 + * LDT. 32 + */ 33 + savesegment(ds, sel); 34 + if ((sel & SEGMENT_TI_MASK) == SEGMENT_LDT) 35 + loadsegment(ds, sel); 36 + 37 + savesegment(es, sel); 38 + if ((sel & SEGMENT_TI_MASK) == SEGMENT_LDT) 39 + loadsegment(es, sel); 40 + #endif 41 + } 42 + 24 43 /* context.lock is held for us, so we don't need any locking. */ 25 44 static void flush_ldt(void *__mm) 26 45 { ··· 51 32 52 33 pc = &mm->context; 53 34 set_ldt(pc->ldt->entries, pc->ldt->nr_entries); 35 + 36 + refresh_ldt_segments(); 54 37 } 55 38 56 39 /* The caller must call finalize_ldt_struct on the result. LDT starts zeroed. */