diff options
author | John Blackwood <john.blackwood@ccur.com> | 2006-04-07 13:50:25 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-04-09 14:53:53 -0400 |
commit | 97c2803c9c694cafbd9f5e43a25903e0abf25188 (patch) | |
tree | e70f35adaae6c95cf5e4557a92ccaa3f827ae2d3 | |
parent | e48c4729d23a026f3711d5e36add5cce894b4913 (diff) |
[PATCH] x86_64: Plug GS leak in arch_prctl()
In linux-2.6.16, we have noticed a problem where the gs base value
returned from an arch_prtcl(ARCH_GET_GS, ...) call will be incorrect if:
- the current/calling task has NOT set its own gs base yet to a
non-zero value,
- some other task that ran on the same processor previously set their
own gs base to a non-zero value.
In this situation, the ARCH_GET_GS code will read and return the
MSR_KERNEL_GS_BASE msr register.
However, since the __switch_to() code does NOT load/zero the
MSR_KERNEL_GS_BASE register when the task that is switched IN has a zero
next->gs value, the caller of arch_prctl(ARCH_GET_GS, ...) will get back
the value of some previous tasks's gs base value instead of 0.
Change the arch_prctl() ARCH_GET_GS code to only read and return
the MSR_KERNEL_GS_BASE msr register if the 'gs' register of the calling
task is non-zero.
Side note: Since in addition to using arch_prctl(ARCH_SET_GS, ...),
a task can also setup a gs base value by using modify_ldt() and write
an index value into 'gs' from user space, the patch below reads
'gs' instead of using thread.gs, since in the modify_ldt() case,
the thread.gs value will be 0, and incorrect value would be returned
(the task->thread.gs value).
When the user has not set its own gs base value and the 'gs'
register is zero, then the MSR_KERNEL_GS_BASE register will not be
read and a value of zero will be returned by reading and returning
'task->thread.gs'.
The first patch shown below is an attempt at implementing this
approach.
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | arch/x86_64/kernel/process.c | 10 |
1 files changed, 8 insertions, 2 deletions
diff --git a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c index 70dd8e5c6889..1c44b53cb15b 100644 --- a/arch/x86_64/kernel/process.c +++ b/arch/x86_64/kernel/process.c | |||
@@ -781,10 +781,16 @@ long do_arch_prctl(struct task_struct *task, int code, unsigned long addr) | |||
781 | } | 781 | } |
782 | case ARCH_GET_GS: { | 782 | case ARCH_GET_GS: { |
783 | unsigned long base; | 783 | unsigned long base; |
784 | unsigned gsindex; | ||
784 | if (task->thread.gsindex == GS_TLS_SEL) | 785 | if (task->thread.gsindex == GS_TLS_SEL) |
785 | base = read_32bit_tls(task, GS_TLS); | 786 | base = read_32bit_tls(task, GS_TLS); |
786 | else if (doit) | 787 | else if (doit) { |
787 | rdmsrl(MSR_KERNEL_GS_BASE, base); | 788 | asm("movl %%gs,%0" : "=r" (gsindex)); |
789 | if (gsindex) | ||
790 | rdmsrl(MSR_KERNEL_GS_BASE, base); | ||
791 | else | ||
792 | base = task->thread.gs; | ||
793 | } | ||
788 | else | 794 | else |
789 | base = task->thread.gs; | 795 | base = task->thread.gs; |
790 | ret = put_user(base, (unsigned long __user *)addr); | 796 | ret = put_user(base, (unsigned long __user *)addr); |