x86/arch_prctl/64: Remove FSBASE/GSBASE < 4G optimization
As far as I know, the optimization doesn't work on any modern distro because modern distros use high addresses for ASLR. Remove it. The ptrace code was either wrong or very strange, but the behavior with this patch should be essentially identical to the behavior without this patch unless user code goes out of its way to mislead ptrace. On newer CPUs, once the FSGSBASE instructions are enabled, we won't want to use the optimized variant anyway. This isn't actually much of a performance regression, it has no effect on normal dynamically linked programs, and it's a considerably simplification. It also removes some nasty special cases from code that is already way too full of special cases for comfort. Signed-off-by: Andy Lutomirski <luto@kernel.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/dd1599b08866961dba9d2458faa6bbd7fba471d7.1461698311.git.luto@kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Родитель
b038c842b3
Коммит
731e33e39a
|
@ -208,13 +208,6 @@
|
|||
#define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS*8 + 3)
|
||||
#define __PER_CPU_SEG (GDT_ENTRY_PER_CPU*8 + 3)
|
||||
|
||||
/* TLS indexes for 64-bit - hardcoded in arch_prctl(): */
|
||||
#define FS_TLS 0
|
||||
#define GS_TLS 1
|
||||
|
||||
#define GS_TLS_SEL ((GDT_ENTRY_TLS_MIN+GS_TLS)*8 + 3)
|
||||
#define FS_TLS_SEL ((GDT_ENTRY_TLS_MIN+FS_TLS)*8 + 3)
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PARAVIRT
|
||||
|
|
|
@ -136,25 +136,6 @@ void release_thread(struct task_struct *dead_task)
|
|||
}
|
||||
}
|
||||
|
||||
static inline void set_32bit_tls(struct task_struct *t, int tls, u32 addr)
|
||||
{
|
||||
struct user_desc ud = {
|
||||
.base_addr = addr,
|
||||
.limit = 0xfffff,
|
||||
.seg_32bit = 1,
|
||||
.limit_in_pages = 1,
|
||||
.useable = 1,
|
||||
};
|
||||
struct desc_struct *desc = t->thread.tls_array;
|
||||
desc += tls;
|
||||
fill_ldt(desc, &ud);
|
||||
}
|
||||
|
||||
static inline u32 read_32bit_tls(struct task_struct *t, int tls)
|
||||
{
|
||||
return get_desc_base(&t->thread.tls_array[tls]);
|
||||
}
|
||||
|
||||
int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
|
||||
unsigned long arg, struct task_struct *p, unsigned long tls)
|
||||
{
|
||||
|
@ -554,25 +535,12 @@ long do_arch_prctl(struct task_struct *task, int code, unsigned long addr)
|
|||
if (addr >= TASK_SIZE_OF(task))
|
||||
return -EPERM;
|
||||
cpu = get_cpu();
|
||||
/* handle small bases via the GDT because that's faster to
|
||||
switch. */
|
||||
if (addr <= 0xffffffff) {
|
||||
set_32bit_tls(task, GS_TLS, addr);
|
||||
if (doit) {
|
||||
load_TLS(&task->thread, cpu);
|
||||
load_gs_index(GS_TLS_SEL);
|
||||
}
|
||||
task->thread.gsindex = GS_TLS_SEL;
|
||||
task->thread.gs = 0;
|
||||
} else {
|
||||
task->thread.gsindex = 0;
|
||||
task->thread.gs = addr;
|
||||
if (doit) {
|
||||
load_gs_index(0);
|
||||
ret = wrmsrl_safe(MSR_KERNEL_GS_BASE, addr);
|
||||
}
|
||||
task->thread.gsindex = 0;
|
||||
task->thread.gs = addr;
|
||||
if (doit) {
|
||||
load_gs_index(0);
|
||||
ret = wrmsrl_safe(MSR_KERNEL_GS_BASE, addr);
|
||||
}
|
||||
put_cpu();
|
||||
break;
|
||||
case ARCH_SET_FS:
|
||||
/* Not strictly needed for fs, but do it for symmetry
|
||||
|
@ -580,25 +548,12 @@ long do_arch_prctl(struct task_struct *task, int code, unsigned long addr)
|
|||
if (addr >= TASK_SIZE_OF(task))
|
||||
return -EPERM;
|
||||
cpu = get_cpu();
|
||||
/* handle small bases via the GDT because that's faster to
|
||||
switch. */
|
||||
if (addr <= 0xffffffff) {
|
||||
set_32bit_tls(task, FS_TLS, addr);
|
||||
if (doit) {
|
||||
load_TLS(&task->thread, cpu);
|
||||
loadsegment(fs, FS_TLS_SEL);
|
||||
}
|
||||
task->thread.fsindex = FS_TLS_SEL;
|
||||
task->thread.fs = 0;
|
||||
} else {
|
||||
task->thread.fsindex = 0;
|
||||
task->thread.fs = addr;
|
||||
if (doit) {
|
||||
/* set the selector to 0 to not confuse
|
||||
__switch_to */
|
||||
loadsegment(fs, 0);
|
||||
ret = wrmsrl_safe(MSR_FS_BASE, addr);
|
||||
}
|
||||
task->thread.fsindex = 0;
|
||||
task->thread.fs = addr;
|
||||
if (doit) {
|
||||
/* set the selector to 0 to not confuse __switch_to */
|
||||
loadsegment(fs, 0);
|
||||
ret = wrmsrl_safe(MSR_FS_BASE, addr);
|
||||
}
|
||||
put_cpu();
|
||||
break;
|
||||
|
@ -606,8 +561,6 @@ long do_arch_prctl(struct task_struct *task, int code, unsigned long addr)
|
|||
unsigned long base;
|
||||
if (doit)
|
||||
rdmsrl(MSR_FS_BASE, base);
|
||||
else if (task->thread.fsindex == FS_TLS_SEL)
|
||||
base = read_32bit_tls(task, FS_TLS);
|
||||
else
|
||||
base = task->thread.fs;
|
||||
ret = put_user(base, (unsigned long __user *)addr);
|
||||
|
@ -617,8 +570,6 @@ long do_arch_prctl(struct task_struct *task, int code, unsigned long addr)
|
|||
unsigned long base;
|
||||
if (doit)
|
||||
rdmsrl(MSR_KERNEL_GS_BASE, base);
|
||||
else if (task->thread.gsindex == GS_TLS_SEL)
|
||||
base = read_32bit_tls(task, GS_TLS);
|
||||
else
|
||||
base = task->thread.gs;
|
||||
ret = put_user(base, (unsigned long __user *)addr);
|
||||
|
|
|
@ -303,29 +303,11 @@ static int set_segment_reg(struct task_struct *task,
|
|||
|
||||
switch (offset) {
|
||||
case offsetof(struct user_regs_struct,fs):
|
||||
/*
|
||||
* If this is setting fs as for normal 64-bit use but
|
||||
* setting fs_base has implicitly changed it, leave it.
|
||||
*/
|
||||
if ((value == FS_TLS_SEL && task->thread.fsindex == 0 &&
|
||||
task->thread.fs != 0) ||
|
||||
(value == 0 && task->thread.fsindex == FS_TLS_SEL &&
|
||||
task->thread.fs == 0))
|
||||
break;
|
||||
task->thread.fsindex = value;
|
||||
if (task == current)
|
||||
loadsegment(fs, task->thread.fsindex);
|
||||
break;
|
||||
case offsetof(struct user_regs_struct,gs):
|
||||
/*
|
||||
* If this is setting gs as for normal 64-bit use but
|
||||
* setting gs_base has implicitly changed it, leave it.
|
||||
*/
|
||||
if ((value == GS_TLS_SEL && task->thread.gsindex == 0 &&
|
||||
task->thread.gs != 0) ||
|
||||
(value == 0 && task->thread.gsindex == GS_TLS_SEL &&
|
||||
task->thread.gs == 0))
|
||||
break;
|
||||
task->thread.gsindex = value;
|
||||
if (task == current)
|
||||
load_gs_index(task->thread.gsindex);
|
||||
|
@ -453,31 +435,17 @@ static unsigned long getreg(struct task_struct *task, unsigned long offset)
|
|||
#ifdef CONFIG_X86_64
|
||||
case offsetof(struct user_regs_struct, fs_base): {
|
||||
/*
|
||||
* do_arch_prctl may have used a GDT slot instead of
|
||||
* the MSR. To userland, it appears the same either
|
||||
* way, except the %fs segment selector might not be 0.
|
||||
* XXX: This will not behave as expected if called on
|
||||
* current or if fsindex != 0.
|
||||
*/
|
||||
unsigned int seg = task->thread.fsindex;
|
||||
if (task->thread.fs != 0)
|
||||
return task->thread.fs;
|
||||
if (task == current)
|
||||
asm("movl %%fs,%0" : "=r" (seg));
|
||||
if (seg != FS_TLS_SEL)
|
||||
return 0;
|
||||
return get_desc_base(&task->thread.tls_array[FS_TLS]);
|
||||
return task->thread.fs;
|
||||
}
|
||||
case offsetof(struct user_regs_struct, gs_base): {
|
||||
/*
|
||||
* Exactly the same here as the %fs handling above.
|
||||
* XXX: This will not behave as expected if called on
|
||||
* current or if fsindex != 0.
|
||||
*/
|
||||
unsigned int seg = task->thread.gsindex;
|
||||
if (task->thread.gs != 0)
|
||||
return task->thread.gs;
|
||||
if (task == current)
|
||||
asm("movl %%gs,%0" : "=r" (seg));
|
||||
if (seg != GS_TLS_SEL)
|
||||
return 0;
|
||||
return get_desc_base(&task->thread.tls_array[GS_TLS]);
|
||||
return task->thread.gs;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче