From owner-svn-src-stable-10@freebsd.org Wed Aug 5 08:17:13 2015 Return-Path: Delivered-To: svn-src-stable-10@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id A814A9B4A79; Wed, 5 Aug 2015 08:17:13 +0000 (UTC) (envelope-from kib@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 9677E7F2; Wed, 5 Aug 2015 08:17:13 +0000 (UTC) (envelope-from kib@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.70]) by repo.freebsd.org (8.14.9/8.14.9) with ESMTP id t758HDJP001064; Wed, 5 Aug 2015 08:17:13 GMT (envelope-from kib@FreeBSD.org) Received: (from kib@localhost) by repo.freebsd.org (8.14.9/8.14.9/Submit) id t758HBsD001055; Wed, 5 Aug 2015 08:17:11 GMT (envelope-from kib@FreeBSD.org) Message-Id: <201508050817.t758HBsD001055@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: kib set sender to kib@FreeBSD.org using -f From: Konstantin Belousov Date: Wed, 5 Aug 2015 08:17:11 +0000 (UTC) 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 X-SVN-Group: stable-10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable-10@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for only the 10-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 05 Aug 2015 08:17:13 -0000 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 #include #include +#include +#include #include #include +#include +#include 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 #include #include +#include #include #include @@ -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 {