Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 5 Aug 2015 08:17:11 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r286311 - in stable/10: lib/libc/sys sys/amd64/amd64 sys/i386/i386 sys/i386/include sys/x86/include
Message-ID:  <201508050817.t758HBsD001055@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Wed Aug  5 08:17:10 2015
New Revision: 286311
URL: https://svnweb.freebsd.org/changeset/base/286311

Log:
  Implement x86 ptrace(2) requests PT_{GET,SET}{FS,GS}BASE.
  
  MFC r284918:
  Add helper fill_based_sd(9).
  
  MFC r284919:
  Add x86 PT_GETFSBASE, PT_GETGSBASE machine-depended ptrace requests to
  obtain the thread %fs and %gs bases.  Add x86 PT_SETFSBASE and
  PT_SETGSBASE requests to set the bases from debuggers.  The set
  requests, similarly to the sysarch({I386,AMD64}_SET_FSBASE), override
  the corresponding segment registers.
  
  MFC r284965:
  Document x86 machine-specific ptrace(2) requests.
  
  MFC r285011:
  Disallow a debugger on 64bit system to set fs/gs bases of the 32bit
  process beyond the end of the process address space.
  
  MFC r285104:
  Grammar and language fixes.

Modified:
  stable/10/lib/libc/sys/ptrace.2
  stable/10/sys/amd64/amd64/ptrace_machdep.c
  stable/10/sys/i386/i386/ptrace_machdep.c
  stable/10/sys/i386/i386/sys_machdep.c
  stable/10/sys/i386/include/md_var.h
  stable/10/sys/x86/include/ptrace.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/lib/libc/sys/ptrace.2
==============================================================================
--- stable/10/lib/libc/sys/ptrace.2	Wed Aug  5 07:37:06 2015	(r286310)
+++ stable/10/lib/libc/sys/ptrace.2	Wed Aug  5 08:17:10 2015	(r286311)
@@ -2,7 +2,7 @@
 .\"	$NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $
 .\"
 .\" This file is in the public domain.
-.Dd July 22, 2013
+.Dd July 3, 2015
 .Dt PTRACE 2
 .Os
 .Sh NAME
@@ -503,8 +503,163 @@ The
 .Fa data
 argument is ignored.
 .El
+.Sh x86 MACHINE-SPECIFIC REQUESTS
+.Bl -tag -width "Dv PT_GETXSTATE_INFO"
+.It Dv PT_GETXMMREGS
+Copy the XMM FPU state into the buffer pointed to by the
+argument
+.Fa addr .
+The buffer has the same layout as the 32-bit save buffer for the
+machine instruction
+.Dv FXSAVE .
+.Pp
+This request is only valid for i386 programs, both on native 32-bit
+systems and on amd64 kernels.
+For 64-bit amd64 programs, the XMM state is reported as part of
+the FPU state returned by the
+.Dv PT_GETFPREGS
+request.
+.Pp
+The
+.Fa data
+argument is ignored.
+.It Dv PT_SETXMMREGS
+Load the XMM FPU state for the thread from the buffer pointed to
+by the argument
+.Fa addr .
+The buffer has the same layout as the 32-bit load buffer for the
+machine instruction
+.Dv FXRSTOR .
+.Pp
+As with
+.Dv PT_GETXMMREGS,
+this request is only valid for i386 programs.
+.Pp
+The
+.Fa data
+argument is ignored.
+.It Dv PT_GETXSTATE_INFO
+Report which XSAVE FPU extensions are supported by the CPU
+and allowed in userspace programs.
+The
+.Fa addr
+argument must point to a variable of type
+.Vt struct ptrace_xstate_info ,
+which contains the information on the request return.
+.Vt struct ptrace_xstate_info
+is defined as follows:
+.Bd -literal
+struct ptrace_xstate_info {
+	uint64_t	xsave_mask;
+	uint32_t	xsave_len;
+};
+.Ed
+The
+.Dv xsave_mask
+field is a bitmask of the currently enabled extensions.
+The meaning of the bits is defined in the Intel and AMD
+processor documentation.
+The
+.Dv xsave_len
+field reports the length of the XSAVE area for storing the hardware
+state for currently enabled extensions in the format defined by the x86
+.Dv XSAVE
+machine instruction.
+.Pp
+The
+.Fa data
+argument value must be equal to the size of the
+.Vt struct ptrace_xstate_info .
+.It Dv PT_GETXSTATE
+Return the content of the XSAVE area for the thread.
+The
+.Fa addr
+argument points to the buffer where the content is copied, and the
+.Fa data
+argument specifies the size of the buffer.
+The kernel copies out as much content as allowed by the buffer size.
+The buffer layout is specified by the layout of the save area for the
+.Dv XSAVE
+machine instruction.
+.It Dv PT_SETXSTATE
+Load the XSAVE state for the thread from the buffer specified by the
+.Fa addr
+pointer.
+The buffer size is passed in the
+.Fa data
+argument.
+The buffer must be at least as large as the
+.Vt struct savefpu
+(defined in
+.Pa x86/fpu.h )
+to allow the complete x87 FPU and XMM state load.
+It must not be larger than the XSAVE state length, as reported by the
+.Dv xsave_len
+field from the
+.Vt struct ptrace_xstate_info
+of the
+.Dv PT_GETXSTATE_INFO
+request.
+Layout of the buffer is identical to the layout of the load area for the
+.Dv XRSTOR
+machine instruction.
+.It Dv PT_GETFSBASE
+Return the value of the base used when doing segmented
+memory addressing using the %fs segment register.
+The
+.Fa addr
+argument points to an
+.Vt unsigned long
+variable where the base value is stored.
+.Pp
+The
+.Fa data
+argument is ignored.
+.It Dv PT_GETGSBASE
+Like the
+.Dv PT_GETFSBASE
+request, but returns the base for the %gs segment register.
+.It Dv PT_SETFSBASE
+Set the base for the %fs segment register to the value pointed to
+by the
+.Fa addr
+argument.
+.Fa addr
+must point to the
+.Vt unsigned long
+variable containing the new base.
+.Pp
+The
+.Fa data
+argument is ignored.
+.It Dv PT_SETGSBASE
+Like the
+.Dv PT_SETFSBASE
+request, but sets the base for the %gs segment register.
+.El
+.Sh PowerPC MACHINE-SPECIFIC REQUESTS
+.Bl -tag -width "Dv PT_SETVRREGS"
+.It Dv PT_GETVRREGS
+Return the thread's
+.Dv ALTIVEC
+machine state in the buffer pointed to by
+.Fa addr .
+.Pp
+The
+.Fa data
+argument is ignored.
+.It Dv PT_SETVRREGS
+Set the thread's
+.Dv ALTIVEC
+machine state from the buffer pointed to by
+.Fa addr .
 .Pp
-Additionally, machine-specific requests can exist.
+The
+.Fa data
+argument is ignored.
+.El
+.Pp
+Additionally, other machine-specific requests can exist.
 .Sh RETURN VALUES
 Some requests can cause
 .Fn ptrace
@@ -564,6 +719,38 @@ provided to
 was less than or equal to zero, or larger than the
 .Vt ptrace_lwpinfo
 structure known to the kernel.
+.It
+The size (in
+.Fa data )
+provided to the x86-specific
+.Dv PT_GETXSTATE_INFO
+request was not equal to the size of the
+.Vt struct ptrace_xstate_info .
+.It
+The size (in
+.Fa data )
+provided to the x86-specific
+.Dv PT_SETXSTATE
+request was less than the size of the x87 plus the XMM save area.
+.It
+The size (in
+.Fa data )
+provided to the x86-specific
+.Dv PT_SETXSTATE
+request was larger than returned in the
+.Dv xsave_len
+member of the
+.Vt struct ptrace_xstate_info
+from the
+.Dv PT_GETXSTATE_INFO
+request.
+.It
+The base value, provided to the amd64-specific requests
+.Dv PT_SETFSBASE
+or
+.Dv PT_SETGSBASE ,
+pointed outside of the valid user address space.
+This error will not occur in 32-bit programs.
 .El
 .It Bq Er EBUSY
 .Bl -bullet -compact

Modified: stable/10/sys/amd64/amd64/ptrace_machdep.c
==============================================================================
--- stable/10/sys/amd64/amd64/ptrace_machdep.c	Wed Aug  5 07:37:06 2015	(r286310)
+++ stable/10/sys/amd64/amd64/ptrace_machdep.c	Wed Aug  5 08:17:10 2015	(r286311)
@@ -36,8 +36,12 @@ __FBSDID("$FreeBSD$");
 #include <sys/proc.h>
 #include <sys/ptrace.h>
 #include <sys/sysent.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
 #include <machine/md_var.h>
 #include <machine/pcb.h>
+#include <machine/frame.h>
+#include <machine/vmparam.h>
 
 static int
 cpu_ptrace_xstate(struct thread *td, int req, void *addr, int data)
@@ -110,6 +114,20 @@ cpu_ptrace_xstate(struct thread *td, int
 	return (error);
 }
 
+static void
+cpu_ptrace_setbase(struct thread *td, int req, register_t r)
+{
+
+	if (req == PT_SETFSBASE) {
+		td->td_pcb->pcb_fsbase = r;
+		td->td_frame->tf_fs = _ufssel;
+	} else {
+		td->td_pcb->pcb_gsbase = r;
+		td->td_frame->tf_gs = _ugssel;
+	}
+	set_pcb_flags(td->td_pcb, PCB_FULL_IRET);
+}
+
 #ifdef COMPAT_FREEBSD32
 #define PT_I386_GETXMMREGS	(PT_FIRSTMACH + 0)
 #define PT_I386_SETXMMREGS	(PT_FIRSTMACH + 1)
@@ -118,6 +136,7 @@ static int
 cpu32_ptrace(struct thread *td, int req, void *addr, int data)
 {
 	struct savefpu *fpstate;
+	uint32_t r;
 	int error;
 
 	switch (req) {
@@ -142,6 +161,29 @@ cpu32_ptrace(struct thread *td, int req,
 		error = cpu_ptrace_xstate(td, req, addr, data);
 		break;
 
+	case PT_GETFSBASE:
+	case PT_GETGSBASE:
+		if (!SV_PROC_FLAG(td->td_proc, SV_ILP32)) {
+			error = EINVAL;
+			break;
+		}
+		r = req == PT_GETFSBASE ? td->td_pcb->pcb_fsbase :
+		    td->td_pcb->pcb_gsbase;
+		error = copyout(&r, addr, sizeof(r));
+		break;
+
+	case PT_SETFSBASE:
+	case PT_SETGSBASE:
+		if (!SV_PROC_FLAG(td->td_proc, SV_ILP32)) {
+			error = EINVAL;
+			break;
+		}
+		error = copyin(addr, &r, sizeof(r));
+		if (error != 0)
+			break;
+		cpu_ptrace_setbase(td, req, r);
+		break;
+
 	default:
 		error = EINVAL;
 		break;
@@ -154,6 +196,7 @@ cpu32_ptrace(struct thread *td, int req,
 int
 cpu_ptrace(struct thread *td, int req, void *addr, int data)
 {
+	register_t *r, rv;
 	int error;
 
 #ifdef COMPAT_FREEBSD32
@@ -176,6 +219,25 @@ cpu_ptrace(struct thread *td, int req, v
 		error = cpu_ptrace_xstate(td, req, addr, data);
 		break;
 
+	case PT_GETFSBASE:
+	case PT_GETGSBASE:
+		r = req == PT_GETFSBASE ? &td->td_pcb->pcb_fsbase :
+		    &td->td_pcb->pcb_gsbase;
+		error = copyout(r, addr, sizeof(*r));
+		break;
+
+	case PT_SETFSBASE:
+	case PT_SETGSBASE:
+		error = copyin(addr, &rv, sizeof(rv));
+		if (error != 0)
+			break;
+		if (rv >= td->td_proc->p_sysent->sv_maxuser) {
+			error = EINVAL;
+			break;
+		}
+		cpu_ptrace_setbase(td, req, rv);
+		break;
+
 	default:
 		error = EINVAL;
 		break;

Modified: stable/10/sys/i386/i386/ptrace_machdep.c
==============================================================================
--- stable/10/sys/i386/i386/ptrace_machdep.c	Wed Aug  5 07:37:06 2015	(r286310)
+++ stable/10/sys/i386/i386/ptrace_machdep.c	Wed Aug  5 08:17:10 2015	(r286311)
@@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/malloc.h>
 #include <sys/proc.h>
 #include <sys/ptrace.h>
+#include <machine/frame.h>
 #include <machine/md_var.h>
 #include <machine/pcb.h>
 
@@ -115,8 +116,8 @@ cpu_ptrace_xstate(struct thread *td, int
 }
 #endif
 
-int
-cpu_ptrace(struct thread *td, int req, void *addr, int data)
+static int
+cpu_ptrace_xmm(struct thread *td, int req, void *addr, int data)
 {
 #ifdef CPU_ENABLE_SSE
 	struct savexmm *fpstate;
@@ -155,3 +156,51 @@ cpu_ptrace(struct thread *td, int req, v
 	return (EINVAL);
 #endif
 }
+
+int
+cpu_ptrace(struct thread *td, int req, void *addr, int data)
+{
+	struct segment_descriptor *sdp, sd;
+	register_t r;
+	int error;
+
+	switch (req) {
+	case PT_GETXMMREGS:
+	case PT_SETXMMREGS:
+	case PT_GETXSTATE_OLD:
+	case PT_SETXSTATE_OLD:
+	case PT_GETXSTATE_INFO:
+	case PT_GETXSTATE:
+	case PT_SETXSTATE:
+		error = cpu_ptrace_xmm(td, req, addr, data);
+		break;
+
+	case PT_GETFSBASE:
+	case PT_GETGSBASE:
+		sdp = req == PT_GETFSBASE ? &td->td_pcb->pcb_fsd :
+		    &td->td_pcb->pcb_gsd;
+		r = sdp->sd_hibase << 24 | sdp->sd_lobase;
+		error = copyout(&r, addr, sizeof(r));
+		break;
+
+	case PT_SETFSBASE:
+	case PT_SETGSBASE:
+		error = copyin(addr, &r, sizeof(r));
+		if (error != 0)
+			break;
+		fill_based_sd(&sd, r);
+		if (req == PT_SETFSBASE) {
+			td->td_pcb->pcb_fsd = sd;
+			td->td_frame->tf_fs = GSEL(GUFS_SEL, SEL_UPL);
+		} else {
+			td->td_pcb->pcb_gsd = sd;
+			td->td_pcb->pcb_gs = GSEL(GUGS_SEL, SEL_UPL);
+		}
+		break;
+
+	default:
+		return (EINVAL);
+	}
+
+	return (error);
+}

Modified: stable/10/sys/i386/i386/sys_machdep.c
==============================================================================
--- stable/10/sys/i386/i386/sys_machdep.c	Wed Aug  5 07:37:06 2015	(r286310)
+++ stable/10/sys/i386/i386/sys_machdep.c	Wed Aug  5 08:17:10 2015	(r286311)
@@ -88,6 +88,27 @@ static int i386_set_ldt_data(struct thre
 	union descriptor *descs);
 static int i386_ldt_grow(struct thread *td, int len);
 
+void
+fill_based_sd(struct segment_descriptor *sdp, uint32_t base)
+{
+
+	sdp->sd_lobase = base & 0xffffff;
+	sdp->sd_hibase = (base >> 24) & 0xff;
+#ifdef XEN
+	/* need to do nosegneg like Linux */
+	sdp->sd_lolimit = (HYPERVISOR_VIRT_START >> 12) & 0xffff;
+#else			
+	sdp->sd_lolimit = 0xffff;	/* 4GB limit, wraps around */
+#endif
+	sdp->sd_hilimit = 0xf;
+	sdp->sd_type = SDT_MEMRWA;
+	sdp->sd_dpl = SEL_UPL;
+	sdp->sd_p = 1;
+	sdp->sd_xx = 0;
+	sdp->sd_def32 = 1;
+	sdp->sd_gran = 1;
+}
+
 #ifndef _SYS_SYSPROTO_H_
 struct sysarch_args {
 	int op;
@@ -202,28 +223,14 @@ sysarch(td, uap)
 		break;
 	case I386_SET_FSBASE:
 		error = copyin(uap->parms, &base, sizeof(base));
-		if (!error) {
+		if (error == 0) {
 			/*
 			 * Construct a descriptor and store it in the pcb for
 			 * the next context switch.  Also store it in the gdt
 			 * so that the load of tf_fs into %fs will activate it
 			 * at return to userland.
 			 */
-			sd.sd_lobase = base & 0xffffff;
-			sd.sd_hibase = (base >> 24) & 0xff;
-#ifdef XEN
-			/* need to do nosegneg like Linux */
-			sd.sd_lolimit = (HYPERVISOR_VIRT_START >> 12) & 0xffff;
-#else			
-			sd.sd_lolimit = 0xffff;	/* 4GB limit, wraps around */
-#endif
-			sd.sd_hilimit = 0xf;
-			sd.sd_type  = SDT_MEMRWA;
-			sd.sd_dpl   = SEL_UPL;
-			sd.sd_p     = 1;
-			sd.sd_xx    = 0;
-			sd.sd_def32 = 1;
-			sd.sd_gran  = 1;
+			fill_based_sd(&sd, base);
 			critical_enter();
 			td->td_pcb->pcb_fsd = sd;
 #ifdef XEN
@@ -243,28 +250,13 @@ sysarch(td, uap)
 		break;
 	case I386_SET_GSBASE:
 		error = copyin(uap->parms, &base, sizeof(base));
-		if (!error) {
+		if (error == 0) {
 			/*
 			 * Construct a descriptor and store it in the pcb for
 			 * the next context switch.  Also store it in the gdt
 			 * because we have to do a load_gs() right now.
 			 */
-			sd.sd_lobase = base & 0xffffff;
-			sd.sd_hibase = (base >> 24) & 0xff;
-
-#ifdef XEN
-			/* need to do nosegneg like Linux */
-			sd.sd_lolimit = (HYPERVISOR_VIRT_START >> 12) & 0xffff;
-#else	
-			sd.sd_lolimit = 0xffff;	/* 4GB limit, wraps around */
-#endif
-			sd.sd_hilimit = 0xf;
-			sd.sd_type  = SDT_MEMRWA;
-			sd.sd_dpl   = SEL_UPL;
-			sd.sd_p     = 1;
-			sd.sd_xx    = 0;
-			sd.sd_def32 = 1;
-			sd.sd_gran  = 1;
+			fill_based_sd(&sd, base);
 			critical_enter();
 			td->td_pcb->pcb_gsd = sd;
 #ifdef XEN

Modified: stable/10/sys/i386/include/md_var.h
==============================================================================
--- stable/10/sys/i386/include/md_var.h	Wed Aug  5 07:37:06 2015	(r286310)
+++ stable/10/sys/i386/include/md_var.h	Wed Aug  5 08:17:10 2015	(r286311)
@@ -94,6 +94,7 @@ struct	reg;
 struct	fpreg;
 struct  dbreg;
 struct	dumperinfo;
+struct	segment_descriptor;
 
 void	*alloc_fpusave(int flags);
 void	bcopyb(const void *from, void *to, size_t len);
@@ -112,6 +113,7 @@ void	dump_add_page(vm_paddr_t);
 void	dump_drop_page(vm_paddr_t);
 void	finishidentcpu(void);
 void	fillw(int /*u_short*/ pat, void *base, size_t cnt);
+void	fill_based_sd(struct segment_descriptor *sdp, uint32_t base);
 void	initializecpu(void);
 void	initializecpucache(void);
 void	i686_pagezero(void *addr);

Modified: stable/10/sys/x86/include/ptrace.h
==============================================================================
--- stable/10/sys/x86/include/ptrace.h	Wed Aug  5 07:37:06 2015	(r286310)
+++ stable/10/sys/x86/include/ptrace.h	Wed Aug  5 08:17:10 2015	(r286311)
@@ -51,6 +51,10 @@
 #define	PT_GETXSTATE_INFO (PT_FIRSTMACH + 4)
 #define	PT_GETXSTATE	(PT_FIRSTMACH + 5)
 #define	PT_SETXSTATE	(PT_FIRSTMACH + 6)
+#define	PT_GETFSBASE	(PT_FIRSTMACH + 7)
+#define	PT_SETFSBASE	(PT_FIRSTMACH + 8)
+#define	PT_GETGSBASE	(PT_FIRSTMACH + 9)
+#define	PT_SETGSBASE	(PT_FIRSTMACH + 10)
 
 /* Argument structure for PT_GETXSTATE_INFO. */
 struct ptrace_xstate_info {



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201508050817.t758HBsD001055>