lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 21 Nov 2007 02:21:59 -0800 (PST)
From:	Roland McGrath <roland@...hat.com>
To:	Andrew Morton <akpm@...ux-foundation.org>,
	Thomas Gleixner <tglx@...utronix.de>,
	Ingo Molnar <mingo@...hat.com>,
	"H. Peter Anvin" <hpa@...or.com>
Cc:	linux-kernel@...r.kernel.org
Subject: [PATCH 3/5] 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@...hat.com>

diff --git a/arch/x86/kernel/ptrace_64.c b/arch/x86/kernel/ptrace_64.c
index 607085f..3ff7ddc 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);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ