Forráskód Böngészése

[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>
John Blackwood 19 éve
szülő
commit
97c2803c9c
1 módosított fájl, 8 hozzáadás és 2 törlés
  1. 8 2
      arch/x86_64/kernel/process.c

+ 8 - 2
arch/x86_64/kernel/process.c

@@ -781,10 +781,16 @@ long do_arch_prctl(struct task_struct *task, int code, unsigned long addr)
 	}
 	case ARCH_GET_GS: { 
 		unsigned long base;
+		unsigned gsindex;
 		if (task->thread.gsindex == GS_TLS_SEL)
 			base = read_32bit_tls(task, GS_TLS);
-		else if (doit)
-			rdmsrl(MSR_KERNEL_GS_BASE, base);
+		else if (doit) {
+ 			asm("movl %%gs,%0" : "=r" (gsindex));
+			if (gsindex)
+				rdmsrl(MSR_KERNEL_GS_BASE, base);
+			else
+				base = task->thread.gs;
+		}
 		else
 			base = task->thread.gs;
 		ret = put_user(base, (unsigned long __user *)addr);