From fd40fc39f47ada66231addbd82efc81513ea63e1 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Wed, 30 Jan 2008 13:30:45 +0100 Subject: [PATCH] --- yaml --- r: 79852 b: refs/heads/master c: df5d438e33d7fc914ba9b6e0d6b019a8966c5fcc h: refs/heads/master v: v3 --- [refs] | 2 +- trunk/arch/x86/kernel/ptrace_64.c | 35 +++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/[refs] b/[refs] index 41a1e2de4ad3..e5dd0e4458ca 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 91394eb0975b3771dde7071a0825c6df6c20ff8a +refs/heads/master: df5d438e33d7fc914ba9b6e0d6b019a8966c5fcc diff --git a/trunk/arch/x86/kernel/ptrace_64.c b/trunk/arch/x86/kernel/ptrace_64.c index 607085f3f08a..1edece36044c 100644 --- a/trunk/arch/x86/kernel/ptrace_64.c +++ b/trunk/arch/x86/kernel/ptrace_64.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -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);