aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2008-01-30 07:30:45 -0500
committerIngo Molnar <mingo@elte.hu>2008-01-30 07:30:45 -0500
commitdf5d438e33d7fc914ba9b6e0d6b019a8966c5fcc (patch)
tree337f1ff5433f2ec8c21852d397361f9d05d7efdd /arch
parent91394eb0975b3771dde7071a0825c6df6c20ff8a (diff)
x86: ptrace fs/gs_base
The fs_base and gs_base fields are available in user_regs_struct. But reading these via ptrace (PTRACE_GETREGS or PTRACE_PEEKUSR) does not give a reliably useful value. The thread_struct fields are 0 when do_arch_prctl decided to use a GDT slot instead of MSR_FS_BASE, which it does for a value under 1<<32. This changes ptrace access to fs_base and gs_base to work like PTRACE_ARCH_PRCTL does. That is, it reads the base address that user-mode memory access using the fs/gs instruction prefixes will use, regardless of how it's being implemented in the kernel. The MSR vs GDT is an implementation detail that is pretty much hidden from userland in the actual using, and there is no reason that ptrace should give the internal implementation picture rather than the user-mode semantic picture. In the case of setting the value, this can implicitly change the fsindex/gsindex value (also separately in user_regs_struct), which is what happens when the thread calls arch_prctl itself. In a PTRACE_SETREGS, the fs_base change will come after the fsindex change due to the order of the struct, and so a change the debugger made to fs_base will have the effect intended, another part of the user_regs_struct will now differ when read back from what the debugger wrote. This makes PTRACE_ARCH_PRCTL obsolete. We could consider declaring it deprecated and removing it one day, though there is no hurry. For the foreseeable future, debuggers have to assume an old kernel that does not report reliable fs_base/gs_base values in user_regs_struct and stick to PTRACE_ARCH_PRCTL anyway. Signed-off-by: Roland McGrath <roland@redhat.com> Cc: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/kernel/ptrace_64.c35
1 files changed, 31 insertions, 4 deletions
diff --git a/arch/x86/kernel/ptrace_64.c b/arch/x86/kernel/ptrace_64.c
index 607085f3f08a..1edece36044c 100644
--- a/arch/x86/kernel/ptrace_64.c
+++ b/arch/x86/kernel/ptrace_64.c
@@ -22,6 +22,7 @@
22#include <asm/pgtable.h> 22#include <asm/pgtable.h>
23#include <asm/system.h> 23#include <asm/system.h>
24#include <asm/processor.h> 24#include <asm/processor.h>
25#include <asm/prctl.h>
25#include <asm/i387.h> 26#include <asm/i387.h>
26#include <asm/debugreg.h> 27#include <asm/debugreg.h>
27#include <asm/ldt.h> 28#include <asm/ldt.h>
@@ -260,12 +261,22 @@ static int putreg(struct task_struct *child,
260 case offsetof(struct user_regs_struct,fs_base): 261 case offsetof(struct user_regs_struct,fs_base):
261 if (value >= TASK_SIZE_OF(child)) 262 if (value >= TASK_SIZE_OF(child))
262 return -EIO; 263 return -EIO;
263 child->thread.fs = value; 264 /*
265 * When changing the segment base, use do_arch_prctl
266 * to set either thread.fs or thread.fsindex and the
267 * corresponding GDT slot.
268 */
269 if (child->thread.fs != value)
270 return do_arch_prctl(child, ARCH_SET_FS, value);
264 return 0; 271 return 0;
265 case offsetof(struct user_regs_struct,gs_base): 272 case offsetof(struct user_regs_struct,gs_base):
273 /*
274 * Exactly the same here as the %fs handling above.
275 */
266 if (value >= TASK_SIZE_OF(child)) 276 if (value >= TASK_SIZE_OF(child))
267 return -EIO; 277 return -EIO;
268 child->thread.gs = value; 278 if (child->thread.gs != value)
279 return do_arch_prctl(child, ARCH_SET_GS, value);
269 return 0; 280 return 0;
270 case offsetof(struct user_regs_struct, eflags): 281 case offsetof(struct user_regs_struct, eflags):
271 value &= FLAG_MASK; 282 value &= FLAG_MASK;
@@ -296,9 +307,25 @@ static unsigned long getreg(struct task_struct *child, unsigned long regno)
296 case offsetof(struct user_regs_struct, es): 307 case offsetof(struct user_regs_struct, es):
297 return child->thread.es; 308 return child->thread.es;
298 case offsetof(struct user_regs_struct, fs_base): 309 case offsetof(struct user_regs_struct, fs_base):
299 return child->thread.fs; 310 /*
311 * do_arch_prctl may have used a GDT slot instead of
312 * the MSR. To userland, it appears the same either
313 * way, except the %fs segment selector might not be 0.
314 */
315 if (child->thread.fs != 0)
316 return child->thread.fs;
317 if (child->thread.fsindex != FS_TLS_SEL)
318 return 0;
319 return get_desc_base(&child->thread.tls_array[FS_TLS]);
300 case offsetof(struct user_regs_struct, gs_base): 320 case offsetof(struct user_regs_struct, gs_base):
301 return child->thread.gs; 321 /*
322 * Exactly the same here as the %fs handling above.
323 */
324 if (child->thread.gs != 0)
325 return child->thread.gs;
326 if (child->thread.gsindex != GS_TLS_SEL)
327 return 0;
328 return get_desc_base(&child->thread.tls_array[GS_TLS]);
302 default: 329 default:
303 regno = regno - sizeof(struct pt_regs); 330 regno = regno - sizeof(struct pt_regs);
304 val = get_stack_long(child, regno); 331 val = get_stack_long(child, regno);