diff options
-rw-r--r-- | arch/x86/kernel/ptrace_64.c | 35 |
1 files changed, 31 insertions, 4 deletions
diff --git a/arch/x86/kernel/ptrace_64.c b/arch/x86/kernel/ptrace_64.c index 607085f3f08..1edece36044 100644 --- a/arch/x86/kernel/ptrace_64.c +++ b/arch/x86/kernel/ptrace_64.c @@ -22,6 +22,7 @@ #include <asm/pgtable.h> #include <asm/system.h> #include <asm/processor.h> +#include <asm/prctl.h> #include <asm/i387.h> #include <asm/debugreg.h> #include <asm/ldt.h> @@ -260,12 +261,22 @@ static int putreg(struct task_struct *child, case offsetof(struct user_regs_struct,fs_base): if (value >= TASK_SIZE_OF(child)) return -EIO; - child->thread.fs = value; + /* + * When changing the segment base, use do_arch_prctl + * to set either thread.fs or thread.fsindex and the + * corresponding GDT slot. + */ + if (child->thread.fs != value) + return do_arch_prctl(child, ARCH_SET_FS, value); return 0; case offsetof(struct user_regs_struct,gs_base): + /* + * Exactly the same here as the %fs handling above. + */ if (value >= TASK_SIZE_OF(child)) return -EIO; - child->thread.gs = value; + if (child->thread.gs != value) + return do_arch_prctl(child, ARCH_SET_GS, value); return 0; case offsetof(struct user_regs_struct, eflags): value &= FLAG_MASK; @@ -296,9 +307,25 @@ static unsigned long getreg(struct task_struct *child, unsigned long regno) case offsetof(struct user_regs_struct, es): return child->thread.es; case offsetof(struct user_regs_struct, fs_base): - return child->thread.fs; + /* + * 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. + */ + if (child->thread.fs != 0) + return child->thread.fs; + if (child->thread.fsindex != FS_TLS_SEL) + return 0; + return get_desc_base(&child->thread.tls_array[FS_TLS]); case offsetof(struct user_regs_struct, gs_base): - return child->thread.gs; + /* + * Exactly the same here as the %fs handling above. + */ + if (child->thread.gs != 0) + return child->thread.gs; + if (child->thread.gsindex != GS_TLS_SEL) + return 0; + return get_desc_base(&child->thread.tls_array[GS_TLS]); default: regno = regno - sizeof(struct pt_regs); val = get_stack_long(child, regno); |