aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChang S. Bae <chang.seok.bae@intel.com>2018-11-26 14:55:24 -0500
committerThomas Gleixner <tglx@linutronix.de>2018-12-18 08:26:09 -0500
commit87ab4689ca6526079ab6f5150219ee88b42000ae (patch)
tree585d3e7a0524b541de9a006d015a2ec7bc1984e6
parent721066dfd4d5c0fee5772c777d6930d0f423b4eb (diff)
x86/fsgsbase/64: Fix the base write helper functions
Andy spotted a regression in the fs/gs base helpers after the patch series was committed. The helper functions which write fs/gs base are not just writing the base, they are also changing the index. That's wrong and needs to be separated because writing the base has not to modify the index. While the regression is not causing any harm right now because the only caller depends on that behaviour, it's a guarantee for subtle breakage down the road. Make the index explicitly changed from the caller, instead of including the code in the helpers. Subsequently, the task write helpers do not handle for the current task anymore. The range check for a base value is also factored out, to minimize code redundancy from the caller. Fixes: b1378a561fd1 ("x86/fsgsbase/64: Introduce FS/GS base helper functions") Suggested-by: Andy Lutomirski <luto@kernel.org> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Andy Lutomirski <luto@kernel.org> Cc: "H . Peter Anvin" <hpa@zytor.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: Ravi Shankar <ravi.v.shankar@intel.com> Cc: H. Peter Anvin <hpa@zytor.com> Link: https://lkml.kernel.org/r/20181126195524.32179-1-chang.seok.bae@intel.com
-rw-r--r--arch/x86/include/asm/fsgsbase.h15
-rw-r--r--arch/x86/kernel/process_64.c99
-rw-r--r--arch/x86/kernel/ptrace.c9
3 files changed, 71 insertions, 52 deletions
diff --git a/arch/x86/include/asm/fsgsbase.h b/arch/x86/include/asm/fsgsbase.h
index eb377b6e9eed..bca4c743de77 100644
--- a/arch/x86/include/asm/fsgsbase.h
+++ b/arch/x86/include/asm/fsgsbase.h
@@ -16,8 +16,8 @@
16 */ 16 */
17extern unsigned long x86_fsbase_read_task(struct task_struct *task); 17extern unsigned long x86_fsbase_read_task(struct task_struct *task);
18extern unsigned long x86_gsbase_read_task(struct task_struct *task); 18extern unsigned long x86_gsbase_read_task(struct task_struct *task);
19extern int x86_fsbase_write_task(struct task_struct *task, unsigned long fsbase); 19extern void x86_fsbase_write_task(struct task_struct *task, unsigned long fsbase);
20extern int x86_gsbase_write_task(struct task_struct *task, unsigned long gsbase); 20extern void x86_gsbase_write_task(struct task_struct *task, unsigned long gsbase);
21 21
22/* Helper functions for reading/writing FS/GS base */ 22/* Helper functions for reading/writing FS/GS base */
23 23
@@ -39,8 +39,15 @@ static inline unsigned long x86_gsbase_read_cpu_inactive(void)
39 return gsbase; 39 return gsbase;
40} 40}
41 41
42extern void x86_fsbase_write_cpu(unsigned long fsbase); 42static inline void x86_fsbase_write_cpu(unsigned long fsbase)
43extern void x86_gsbase_write_cpu_inactive(unsigned long gsbase); 43{
44 wrmsrl(MSR_FS_BASE, fsbase);
45}
46
47static inline void x86_gsbase_write_cpu_inactive(unsigned long gsbase)
48{
49 wrmsrl(MSR_KERNEL_GS_BASE, gsbase);
50}
44 51
45#endif /* CONFIG_X86_64 */ 52#endif /* CONFIG_X86_64 */
46 53
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index bbfbf017065c..ddd4fa718c43 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -339,24 +339,6 @@ static unsigned long x86_fsgsbase_read_task(struct task_struct *task,
339 return base; 339 return base;
340} 340}
341 341
342void x86_fsbase_write_cpu(unsigned long fsbase)
343{
344 /*
345 * Set the selector to 0 as a notion, that the segment base is
346 * overwritten, which will be checked for skipping the segment load
347 * during context switch.
348 */
349 loadseg(FS, 0);
350 wrmsrl(MSR_FS_BASE, fsbase);
351}
352
353void x86_gsbase_write_cpu_inactive(unsigned long gsbase)
354{
355 /* Set the selector to 0 for the same reason as %fs above. */
356 loadseg(GS, 0);
357 wrmsrl(MSR_KERNEL_GS_BASE, gsbase);
358}
359
360unsigned long x86_fsbase_read_task(struct task_struct *task) 342unsigned long x86_fsbase_read_task(struct task_struct *task)
361{ 343{
362 unsigned long fsbase; 344 unsigned long fsbase;
@@ -385,38 +367,18 @@ unsigned long x86_gsbase_read_task(struct task_struct *task)
385 return gsbase; 367 return gsbase;
386} 368}
387 369
388int x86_fsbase_write_task(struct task_struct *task, unsigned long fsbase) 370void x86_fsbase_write_task(struct task_struct *task, unsigned long fsbase)
389{ 371{
390 /* 372 WARN_ON_ONCE(task == current);
391 * Not strictly needed for %fs, but do it for symmetry
392 * with %gs
393 */
394 if (unlikely(fsbase >= TASK_SIZE_MAX))
395 return -EPERM;
396 373
397 preempt_disable();
398 task->thread.fsbase = fsbase; 374 task->thread.fsbase = fsbase;
399 if (task == current)
400 x86_fsbase_write_cpu(fsbase);
401 task->thread.fsindex = 0;
402 preempt_enable();
403
404 return 0;
405} 375}
406 376
407int x86_gsbase_write_task(struct task_struct *task, unsigned long gsbase) 377void x86_gsbase_write_task(struct task_struct *task, unsigned long gsbase)
408{ 378{
409 if (unlikely(gsbase >= TASK_SIZE_MAX)) 379 WARN_ON_ONCE(task == current);
410 return -EPERM;
411 380
412 preempt_disable();
413 task->thread.gsbase = gsbase; 381 task->thread.gsbase = gsbase;
414 if (task == current)
415 x86_gsbase_write_cpu_inactive(gsbase);
416 task->thread.gsindex = 0;
417 preempt_enable();
418
419 return 0;
420} 382}
421 383
422int copy_thread_tls(unsigned long clone_flags, unsigned long sp, 384int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
@@ -754,11 +716,60 @@ long do_arch_prctl_64(struct task_struct *task, int option, unsigned long arg2)
754 716
755 switch (option) { 717 switch (option) {
756 case ARCH_SET_GS: { 718 case ARCH_SET_GS: {
757 ret = x86_gsbase_write_task(task, arg2); 719 if (unlikely(arg2 >= TASK_SIZE_MAX))
720 return -EPERM;
721
722 preempt_disable();
723 /*
724 * ARCH_SET_GS has always overwritten the index
725 * and the base. Zero is the most sensible value
726 * to put in the index, and is the only value that
727 * makes any sense if FSGSBASE is unavailable.
728 */
729 if (task == current) {
730 loadseg(GS, 0);
731 x86_gsbase_write_cpu_inactive(arg2);
732
733 /*
734 * On non-FSGSBASE systems, save_base_legacy() expects
735 * that we also fill in thread.gsbase.
736 */
737 task->thread.gsbase = arg2;
738
739 } else {
740 task->thread.gsindex = 0;
741 x86_gsbase_write_task(task, arg2);
742 }
743 preempt_enable();
758 break; 744 break;
759 } 745 }
760 case ARCH_SET_FS: { 746 case ARCH_SET_FS: {
761 ret = x86_fsbase_write_task(task, arg2); 747 /*
748 * Not strictly needed for %fs, but do it for symmetry
749 * with %gs
750 */
751 if (unlikely(arg2 >= TASK_SIZE_MAX))
752 return -EPERM;
753
754 preempt_disable();
755 /*
756 * Set the selector to 0 for the same reason
757 * as %gs above.
758 */
759 if (task == current) {
760 loadseg(FS, 0);
761 x86_fsbase_write_cpu(arg2);
762
763 /*
764 * On non-FSGSBASE systems, save_base_legacy() expects
765 * that we also fill in thread.fsbase.
766 */
767 task->thread.fsbase = arg2;
768 } else {
769 task->thread.fsindex = 0;
770 x86_fsbase_write_task(task, arg2);
771 }
772 preempt_enable();
762 break; 773 break;
763 } 774 }
764 case ARCH_GET_FS: { 775 case ARCH_GET_FS: {
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index ffae9b9740fd..4b8ee05dd6ad 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -397,11 +397,12 @@ static int putreg(struct task_struct *child,
397 if (value >= TASK_SIZE_MAX) 397 if (value >= TASK_SIZE_MAX)
398 return -EIO; 398 return -EIO;
399 /* 399 /*
400 * When changing the FS base, use the same 400 * When changing the FS base, use do_arch_prctl_64()
401 * mechanism as for do_arch_prctl_64(). 401 * to set the index to zero and to set the base
402 * as requested.
402 */ 403 */
403 if (child->thread.fsbase != value) 404 if (child->thread.fsbase != value)
404 return x86_fsbase_write_task(child, value); 405 return do_arch_prctl_64(child, ARCH_SET_FS, value);
405 return 0; 406 return 0;
406 case offsetof(struct user_regs_struct,gs_base): 407 case offsetof(struct user_regs_struct,gs_base):
407 /* 408 /*
@@ -410,7 +411,7 @@ static int putreg(struct task_struct *child,
410 if (value >= TASK_SIZE_MAX) 411 if (value >= TASK_SIZE_MAX)
411 return -EIO; 412 return -EIO;
412 if (child->thread.gsbase != value) 413 if (child->thread.gsbase != value)
413 return x86_gsbase_write_task(child, value); 414 return do_arch_prctl_64(child, ARCH_SET_GS, value);
414 return 0; 415 return 0;
415#endif 416#endif
416 } 417 }