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.
This commit is contained in:
parent
13079235af
commit
83f30eda37
@ -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
|
||||
Additionally, machine-specific requests can exist.
|
||||
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
|
||||
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
|
||||
|
@ -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 req, void *addr, int data)
|
||||
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, void *addr, int data)
|
||||
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, void *addr, int data)
|
||||
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, void *addr, int data)
|
||||
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;
|
||||
|
@ -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 req, void *addr, int data)
|
||||
}
|
||||
#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, void *addr, int data)
|
||||
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);
|
||||
}
|
||||
|
@ -88,6 +88,27 @@ static int i386_set_ldt_data(struct thread *, int start, int num,
|
||||
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
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user