Changeset View
Standalone View
sys/kern/sys_process.c
Show First 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | struct ptrace_vm_entry32 { | ||||
uint32_t pve_end; | uint32_t pve_end; | ||||
uint32_t pve_offset; | uint32_t pve_offset; | ||||
u_int pve_prot; | u_int pve_prot; | ||||
u_int pve_pathlen; | u_int pve_pathlen; | ||||
int32_t pve_fileid; | int32_t pve_fileid; | ||||
u_int pve_fsid; | u_int pve_fsid; | ||||
uint32_t pve_path; | uint32_t pve_path; | ||||
}; | }; | ||||
struct iovec32 { | |||||
uint32_t iov_base; /* Base address. */ | |||||
uint32_t iov_len; /* Length. */ | |||||
}; | |||||
#endif | #endif | ||||
struct ptrace_regset { | |||||
void *prs_addr; | |||||
size_t prs_size; | |||||
}; | |||||
/* | /* | ||||
* Functions implemented using PROC_ACTION(): | * Functions implemented using PROC_ACTION(): | ||||
* | * | ||||
* proc_read_regs(proc, regs) | * proc_read_regs(proc, regs) | ||||
* Get the current user-visible register set from the process | * Get the current user-visible register set from the process | ||||
* and copy it into the regs structure (<machine/reg.h>). | * and copy it into the regs structure (<machine/reg.h>). | ||||
* The process is stopped at the time read_regs is called. | * The process is stopped at the time read_regs is called. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | |||||
int | int | ||||
proc_write_fpregs(struct thread *td, struct fpreg *fpregs) | proc_write_fpregs(struct thread *td, struct fpreg *fpregs) | ||||
{ | { | ||||
PROC_ACTION(set_fpregs(td, fpregs)); | PROC_ACTION(set_fpregs(td, fpregs)); | ||||
} | } | ||||
SET_DECLARE(elf_regset, struct regset); | |||||
#ifdef COMPAT_FREEBSD32 | #ifdef COMPAT_FREEBSD32 | ||||
SET_DECLARE(elf_regset_compat32, struct regset); | |||||
#endif | |||||
static struct regset * | |||||
proc_regset_find(int note, int compat32) | |||||
{ | |||||
struct regset **regsetpp, *regsetp; | |||||
#ifdef COMPAT_FREEBSD32 | |||||
if (compat32) { | |||||
SET_FOREACH(regsetpp, elf_regset_compat32) { | |||||
regsetp = *regsetpp; | |||||
if (regsetp->note != note) | |||||
continue; | |||||
return (regsetp); | |||||
} | |||||
} else | |||||
#endif | |||||
{ | |||||
SET_FOREACH(regsetpp, elf_regset) { | |||||
regsetp = *regsetpp; | |||||
if (regsetp->note != note) | |||||
continue; | |||||
return (regsetp); | |||||
} | |||||
} | |||||
return (NULL); | |||||
} | |||||
static size_t | |||||
proc_regset_maxsize(int note, int compat32) | |||||
{ | |||||
struct regset *regset; | |||||
regset = proc_regset_find(note, compat32); | |||||
if (regset != NULL) | |||||
return (regset->size); | |||||
return (0); | |||||
} | |||||
jhb: We may consider in the future removing this check and always passing down 'vec->iov_len' as the… | |||||
Done Inline ActionsThe intention is for userspace to query the size by passing in a NULL base and for it to handle data that's not the size it was expecting, e.g. new data was added to the kernel userspace doesn't expect. I have an initial patch for lldb to use this interface on arm64, so will make sure it can handle incorrectly sized data. andrew: The intention is for userspace to query the size by passing in a NULL base and for it to handle… | |||||
Not Done Inline ActionsI don't know that userland is capable of handling forwards compatibility in that way unless we promise userland that if a register set grows beyond a a size it expects, the original N bytes it understands still have the same meaning. OTOH, if the kernel is the one to manage compatibility, it will know what the old format an old application might expect is and be able to read/write the old format. This is generally consistent with what we do with other compat shims where the kernel is the one that deals with this, not applications. However, perhaps we won't ever grow NT_PRSTATUS and instead just need to handle the variable-sized cases like XSAVE and SVE where the register set size is not a compile-time constant. jhb: I don't know that userland is capable of handling forwards compatibility in that way unless we… | |||||
Done Inline ActionsWouldn't userspace already have to handle it when reading a coredump? I was testing on Linux to see what they expose & found it to be just the registers. You can ask for any length that is a multiple of the base register size, e.g. on amd64 it is 27 * 8 byte registers, so you could pass in any multiple of 8 & get up to 216 bytes of register data back. andrew: Wouldn't userspace already have to handle it when reading a coredump?
I was testing on Linux… | |||||
static int | |||||
proc_read_regset_common(struct thread *td, int note, struct iovec *vec, | |||||
int compat32) | |||||
{ | |||||
struct regset *regset; | |||||
size_t size; | |||||
int err; | |||||
regset = proc_regset_find(note, compat32); | |||||
if (regset == NULL) | |||||
return (EINVAL); | |||||
err = 0; | |||||
size = vec->iov_len; | |||||
if (!regset->get(regset, td, vec->iov_base, &size)) | |||||
err = EINVAL; | |||||
KASSERT(size == vec->iov_len, | |||||
("proc_read_regset: Get function changed the size")); | |||||
return (err); | |||||
} | |||||
static int | |||||
proc_read_regset(struct thread *td, int note, struct iovec *vec) | |||||
{ | |||||
return (proc_read_regset_common(td, note, vec, 0)); | |||||
} | |||||
static int | |||||
proc_write_regset_common(struct thread *td, int note, struct iovec *vec, | |||||
int compat32) | |||||
{ | |||||
struct regset *regset; | |||||
size_t size; | |||||
int err; | |||||
regset = proc_regset_find(note, compat32); | |||||
if (regset == NULL) | |||||
return (EINVAL); | |||||
err = 0; | |||||
size = vec->iov_len; | |||||
if (!regset->set(regset, td, vec->iov_base, &size)) | |||||
err = EINVAL; | |||||
KASSERT(size == vec->iov_len, | |||||
("proc_write_regset: Set function changed the size")); | |||||
return (err); | |||||
} | |||||
static int | |||||
proc_write_regset(struct thread *td, int note, struct iovec *vec) | |||||
{ | |||||
return (proc_write_regset_common(td, note, vec, 0)); | |||||
} | |||||
#ifdef COMPAT_FREEBSD32 | |||||
/* For 32 bit binaries, we need to expose the 32 bit regs layouts. */ | /* For 32 bit binaries, we need to expose the 32 bit regs layouts. */ | ||||
Not Done Inline ActionsI am not sure this is safe. At very least, you could loose the rights to debug the target. Since some time, we block parallel ptrace(2) requests for the same process, so I do not think that PT_DETACH could occur. But e.g. thread could be reassigned to different process etc. kib: I am not sure this is safe. At very least, you could loose the rights to debug the target. | |||||
int | int | ||||
proc_read_regs32(struct thread *td, struct reg32 *regs32) | proc_read_regs32(struct thread *td, struct reg32 *regs32) | ||||
{ | { | ||||
PROC_ACTION(fill_regs32(td, regs32)); | PROC_ACTION(fill_regs32(td, regs32)); | ||||
} | } | ||||
Not Done Inline ActionsThis KASSERT() can be removed since regset->set() doesn't change size. jhb: This KASSERT() can be removed since `regset->set()` doesn't change `size`. | |||||
int | int | ||||
proc_write_regs32(struct thread *td, struct reg32 *regs32) | proc_write_regs32(struct thread *td, struct reg32 *regs32) | ||||
{ | { | ||||
PROC_ACTION(set_regs32(td, regs32)); | PROC_ACTION(set_regs32(td, regs32)); | ||||
} | } | ||||
Show All 19 Lines | |||||
} | } | ||||
int | int | ||||
proc_write_fpregs32(struct thread *td, struct fpreg32 *fpregs32) | proc_write_fpregs32(struct thread *td, struct fpreg32 *fpregs32) | ||||
{ | { | ||||
PROC_ACTION(set_fpregs32(td, fpregs32)); | PROC_ACTION(set_fpregs32(td, fpregs32)); | ||||
} | } | ||||
static int | |||||
proc_read_regset32(struct thread *td, int note, struct iovec *vec) | |||||
{ | |||||
return (proc_read_regset_common(td, note, vec, 1)); | |||||
} | |||||
static int | |||||
proc_write_regset32(struct thread *td, int note, struct iovec *vec) | |||||
{ | |||||
return (proc_write_regset_common(td, note, vec, 1)); | |||||
} | |||||
#endif | #endif | ||||
int | int | ||||
proc_sstep(struct thread *td) | proc_sstep(struct thread *td) | ||||
{ | { | ||||
PROC_ACTION(ptrace_single_step(td)); | PROC_ACTION(ptrace_single_step(td)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 331 Lines • ▼ Show 20 Lines | #define BZERO(a, s) wrap32 ? \ | ||||
bzero(a ## 32, s ## 32) : \ | bzero(a ## 32, s ## 32) : \ | ||||
bzero(a, s) | bzero(a, s) | ||||
#define COPYIN(u, k, s) wrap32 ? \ | #define COPYIN(u, k, s) wrap32 ? \ | ||||
copyin(u, k ## 32, s ## 32) : \ | copyin(u, k ## 32, s ## 32) : \ | ||||
copyin(u, k, s) | copyin(u, k, s) | ||||
#define COPYOUT(k, u, s) wrap32 ? \ | #define COPYOUT(k, u, s) wrap32 ? \ | ||||
copyout(k ## 32, u, s ## 32) : \ | copyout(k ## 32, u, s ## 32) : \ | ||||
copyout(k, u, s) | copyout(k, u, s) | ||||
#define IOV_BASE(v) \ | |||||
(wrap32 ? (void *)(uintptr_t)__CONCAT(v, 32).iov_base : (v).iov_base) | |||||
#define IOV_LEN(v) (wrap32 ? __CONCAT(v, 32).iov_len : (v).iov_len) | |||||
#else | #else | ||||
#define BZERO(a, s) bzero(a, s) | #define BZERO(a, s) bzero(a, s) | ||||
#define COPYIN(u, k, s) copyin(u, k, s) | #define COPYIN(u, k, s) copyin(u, k, s) | ||||
#define COPYOUT(k, u, s) copyout(k, u, s) | #define COPYOUT(k, u, s) copyout(k, u, s) | ||||
#define IOV_BASE(v) (v).iov_base | |||||
#define IOV_LEN(v) (v).iov_len | |||||
#endif | #endif | ||||
int | int | ||||
sys_ptrace(struct thread *td, struct ptrace_args *uap) | sys_ptrace(struct thread *td, struct ptrace_args *uap) | ||||
{ | { | ||||
/* | /* | ||||
* XXX this obfuscation is to reduce stack usage, but the register | * XXX this obfuscation is to reduce stack usage, but the register | ||||
* structs may be too large to put on the stack anyway. | * structs may be too large to put on the stack anyway. | ||||
*/ | */ | ||||
union { | union { | ||||
struct ptrace_io_desc piod; | struct ptrace_io_desc piod; | ||||
struct ptrace_lwpinfo pl; | struct ptrace_lwpinfo pl; | ||||
struct ptrace_vm_entry pve; | struct ptrace_vm_entry pve; | ||||
struct dbreg dbreg; | struct dbreg dbreg; | ||||
struct fpreg fpreg; | struct fpreg fpreg; | ||||
struct reg reg; | struct reg reg; | ||||
struct iovec vec; | |||||
#ifdef COMPAT_FREEBSD32 | #ifdef COMPAT_FREEBSD32 | ||||
struct dbreg32 dbreg32; | struct dbreg32 dbreg32; | ||||
struct fpreg32 fpreg32; | struct fpreg32 fpreg32; | ||||
struct reg32 reg32; | struct reg32 reg32; | ||||
struct ptrace_io_desc32 piod32; | struct ptrace_io_desc32 piod32; | ||||
struct ptrace_lwpinfo32 pl32; | struct ptrace_lwpinfo32 pl32; | ||||
struct ptrace_vm_entry32 pve32; | struct ptrace_vm_entry32 pve32; | ||||
struct iovec32 vec32; | |||||
#endif | #endif | ||||
char args[sizeof(td->td_sa.args)]; | char args[sizeof(td->td_sa.args)]; | ||||
struct ptrace_sc_ret psr; | struct ptrace_sc_ret psr; | ||||
int ptevents; | int ptevents; | ||||
} r; | } r; | ||||
struct iovec kern_vec; | |||||
void *addr; | void *addr; | ||||
int error = 0; | int error = 0; | ||||
#ifdef COMPAT_FREEBSD32 | |||||
int wrap32 = 0; | int wrap32 = 0; | ||||
#ifdef COMPAT_FREEBSD32 | |||||
if (SV_CURPROC_FLAG(SV_ILP32)) | if (SV_CURPROC_FLAG(SV_ILP32)) | ||||
wrap32 = 1; | wrap32 = 1; | ||||
#endif | #endif | ||||
AUDIT_ARG_PID(uap->pid); | AUDIT_ARG_PID(uap->pid); | ||||
AUDIT_ARG_CMD(uap->req); | AUDIT_ARG_CMD(uap->req); | ||||
AUDIT_ARG_VALUE(uap->data); | AUDIT_ARG_VALUE(uap->data); | ||||
addr = &r; | addr = &r; | ||||
kern_vec.iov_base = NULL; | |||||
kern_vec.iov_len = 0; | |||||
switch (uap->req) { | switch (uap->req) { | ||||
case PT_GET_EVENT_MASK: | case PT_GET_EVENT_MASK: | ||||
case PT_LWPINFO: | case PT_LWPINFO: | ||||
case PT_GET_SC_ARGS: | case PT_GET_SC_ARGS: | ||||
case PT_GET_SC_RET: | case PT_GET_SC_RET: | ||||
break; | break; | ||||
case PT_GETREGS: | case PT_GETREGS: | ||||
BZERO(&r.reg, sizeof r.reg); | BZERO(&r.reg, sizeof r.reg); | ||||
break; | break; | ||||
case PT_GETFPREGS: | case PT_GETFPREGS: | ||||
BZERO(&r.fpreg, sizeof r.fpreg); | BZERO(&r.fpreg, sizeof r.fpreg); | ||||
break; | break; | ||||
case PT_GETDBREGS: | case PT_GETDBREGS: | ||||
BZERO(&r.dbreg, sizeof r.dbreg); | BZERO(&r.dbreg, sizeof r.dbreg); | ||||
break; | break; | ||||
case PT_SETREGSET: | |||||
error = COPYIN(uap->addr, &r.vec, sizeof r.vec); | |||||
if (error != 0) | |||||
break; | |||||
kern_vec.iov_len = proc_regset_maxsize(uap->data, wrap32); | |||||
/* Check the regset is valid and the length is correct */ | |||||
if (kern_vec.iov_len == 0 || | |||||
kern_vec.iov_len != IOV_LEN(r.vec)) { | |||||
Not Done Inline Actionsstyle(9) is to use ()'s with sizeof jhb: style(9) is to use ()'s with sizeof | |||||
Done Inline ActionsThis is mostly to be consistent with the style in the switch. I'm planning on adding COMPAT32 support, but will need to add an iovec32 first. This will need this non-style sizeof. andrew: This is mostly to be consistent with the style in the switch. I'm planning on adding COMPAT32… | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
kern_vec.iov_base = malloc(kern_vec.iov_len, M_TEMP, | |||||
M_WAITOK); | |||||
error = copyin(IOV_BASE(r.vec), kern_vec.iov_base, | |||||
kern_vec.iov_len); | |||||
if (error != 0) | |||||
break; | |||||
addr = &kern_vec; | |||||
break; | |||||
case PT_GETREGSET: | |||||
error = COPYIN(uap->addr, &r.vec, sizeof r.vec); | |||||
if (error != 0) | |||||
break; | |||||
kern_vec.iov_len = proc_regset_maxsize(uap->data, wrap32); | |||||
if (kern_vec.iov_len == 0) { | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
/* Just return the size when the base is NULL */ | |||||
if (IOV_BASE(r.vec) == NULL) { | |||||
#ifdef COMPAT_FREEBSD32 | |||||
if (wrap32) | |||||
r.vec32.iov_len = kern_vec.iov_len; | |||||
else | |||||
#endif | |||||
r.vec.iov_len = kern_vec.iov_len; | |||||
error = COPYOUT(&r.vec, uap->addr, sizeof r.vec); | |||||
goto out; | |||||
} | |||||
kern_vec.iov_base = malloc(kern_vec.iov_len, M_TEMP, | |||||
M_WAITOK | M_ZERO); | |||||
addr = &kern_vec; | |||||
break; | |||||
case PT_SETREGS: | case PT_SETREGS: | ||||
error = COPYIN(uap->addr, &r.reg, sizeof r.reg); | error = COPYIN(uap->addr, &r.reg, sizeof r.reg); | ||||
break; | break; | ||||
case PT_SETFPREGS: | case PT_SETFPREGS: | ||||
error = COPYIN(uap->addr, &r.fpreg, sizeof r.fpreg); | error = COPYIN(uap->addr, &r.fpreg, sizeof r.fpreg); | ||||
break; | break; | ||||
case PT_SETDBREGS: | case PT_SETDBREGS: | ||||
error = COPYIN(uap->addr, &r.dbreg, sizeof r.dbreg); | error = COPYIN(uap->addr, &r.dbreg, sizeof r.dbreg); | ||||
Show All 10 Lines | #endif | ||||
case PT_VM_ENTRY: | case PT_VM_ENTRY: | ||||
error = COPYIN(uap->addr, &r.pve, sizeof r.pve); | error = COPYIN(uap->addr, &r.pve, sizeof r.pve); | ||||
break; | break; | ||||
default: | default: | ||||
addr = uap->addr; | addr = uap->addr; | ||||
break; | break; | ||||
} | } | ||||
if (error) | if (error) | ||||
return (error); | goto out; | ||||
error = kern_ptrace(td, uap->req, uap->pid, addr, uap->data); | error = kern_ptrace(td, uap->req, uap->pid, addr, uap->data); | ||||
if (error) | if (error) | ||||
return (error); | goto out; | ||||
Not Done Inline ActionsPerhaps use 'goto out' here instead? jhb: Perhaps use 'goto out' here instead? | |||||
switch (uap->req) { | switch (uap->req) { | ||||
case PT_VM_ENTRY: | case PT_VM_ENTRY: | ||||
error = COPYOUT(&r.pve, uap->addr, sizeof r.pve); | error = COPYOUT(&r.pve, uap->addr, sizeof r.pve); | ||||
break; | break; | ||||
case PT_IO: | case PT_IO: | ||||
error = COPYOUT(&r.piod, uap->addr, sizeof r.piod); | error = COPYOUT(&r.piod, uap->addr, sizeof r.piod); | ||||
break; | break; | ||||
case PT_GETREGS: | case PT_GETREGS: | ||||
error = COPYOUT(&r.reg, uap->addr, sizeof r.reg); | error = COPYOUT(&r.reg, uap->addr, sizeof r.reg); | ||||
break; | break; | ||||
case PT_GETFPREGS: | case PT_GETFPREGS: | ||||
error = COPYOUT(&r.fpreg, uap->addr, sizeof r.fpreg); | error = COPYOUT(&r.fpreg, uap->addr, sizeof r.fpreg); | ||||
break; | break; | ||||
case PT_GETDBREGS: | case PT_GETDBREGS: | ||||
error = COPYOUT(&r.dbreg, uap->addr, sizeof r.dbreg); | error = COPYOUT(&r.dbreg, uap->addr, sizeof r.dbreg); | ||||
break; | break; | ||||
case PT_GETREGSET: | |||||
#ifdef COMPAT_FREEBSD32 | |||||
if (wrap32) | |||||
r.vec32.iov_len = MIN(r.vec32.iov_len,kern_vec.iov_len); | |||||
else | |||||
#endif | |||||
r.vec.iov_len = MIN(r.vec.iov_len, kern_vec.iov_len); | |||||
error = copyout(kern_vec.iov_base, IOV_BASE(r.vec), | |||||
IOV_LEN(r.vec)); | |||||
if (error != 0) | |||||
break; | |||||
error = COPYOUT(&r.vec, uap->addr, sizeof r.vec); | |||||
break; | |||||
case PT_GET_EVENT_MASK: | case PT_GET_EVENT_MASK: | ||||
/* NB: The size in uap->data is validated in kern_ptrace(). */ | /* NB: The size in uap->data is validated in kern_ptrace(). */ | ||||
error = copyout(&r.ptevents, uap->addr, uap->data); | error = copyout(&r.ptevents, uap->addr, uap->data); | ||||
break; | break; | ||||
case PT_LWPINFO: | case PT_LWPINFO: | ||||
/* NB: The size in uap->data is validated in kern_ptrace(). */ | /* NB: The size in uap->data is validated in kern_ptrace(). */ | ||||
error = copyout(&r.pl, uap->addr, uap->data); | error = copyout(&r.pl, uap->addr, uap->data); | ||||
break; | break; | ||||
case PT_GET_SC_ARGS: | case PT_GET_SC_ARGS: | ||||
error = copyout(r.args, uap->addr, MIN(uap->data, | error = copyout(r.args, uap->addr, MIN(uap->data, | ||||
sizeof(r.args))); | sizeof(r.args))); | ||||
break; | break; | ||||
case PT_GET_SC_RET: | case PT_GET_SC_RET: | ||||
error = copyout(&r.psr, uap->addr, MIN(uap->data, | error = copyout(&r.psr, uap->addr, MIN(uap->data, | ||||
sizeof(r.psr))); | sizeof(r.psr))); | ||||
break; | break; | ||||
} | } | ||||
out: | |||||
free(kern_vec.iov_base, M_TEMP); | |||||
return (error); | return (error); | ||||
Not Done Inline ActionsAnd move the free of kern_vec.iov_base to here under an 'out' label. This fixes a leak when the first copyout fails. It will also make adding other functions that use an iovec simpler by consolidating the error handling. jhb: And move the free of kern_vec.iov_base to here under an 'out' label. This fixes a leak when… | |||||
} | } | ||||
#undef COPYIN | #undef COPYIN | ||||
#undef COPYOUT | #undef COPYOUT | ||||
#undef BZERO | #undef BZERO | ||||
#ifdef COMPAT_FREEBSD32 | #ifdef COMPAT_FREEBSD32 | ||||
/* | /* | ||||
* PROC_READ(regs, td2, addr); | * PROC_READ(regs, td2, addr); | ||||
* becomes either: | * becomes either: | ||||
* proc_read_regs(td2, addr); | * proc_read_regs(td2, addr); | ||||
* or | * or | ||||
* proc_read_regs32(td2, addr); | * proc_read_regs32(td2, addr); | ||||
* .. except this is done at runtime. There is an additional | * .. except this is done at runtime. There is an additional | ||||
* complication in that PROC_WRITE disallows 32 bit consumers | * complication in that PROC_WRITE disallows 32 bit consumers | ||||
* from writing to 64 bit address space targets. | * from writing to 64 bit address space targets. | ||||
*/ | */ | ||||
#define PROC_READ(w, t, a) wrap32 ? \ | #define PROC_READ(w, t, a) wrap32 ? \ | ||||
proc_read_ ## w ## 32(t, a) : \ | proc_read_ ## w ## 32(t, a) : \ | ||||
proc_read_ ## w (t, a) | proc_read_ ## w (t, a) | ||||
#define PROC_READ_REGSET(w, t, d, a) wrap32 ? \ | |||||
proc_read_ ## w ## 32(t, d, a) : \ | |||||
proc_read_ ## w (t, d, a) | |||||
#define PROC_WRITE(w, t, a) wrap32 ? \ | #define PROC_WRITE(w, t, a) wrap32 ? \ | ||||
(safe ? proc_write_ ## w ## 32(t, a) : EINVAL ) : \ | (safe ? proc_write_ ## w ## 32(t, a) : EINVAL ) : \ | ||||
proc_write_ ## w (t, a) | proc_write_ ## w (t, a) | ||||
#define PROC_WRITE_REGSET(w, t, d, a) wrap32 ? \ | |||||
(safe ? proc_write_ ## w ## 32(t, d, a) : EINVAL ) : \ | |||||
proc_write_ ## w (t, d, a) | |||||
#else | #else | ||||
#define PROC_READ(w, t, a) proc_read_ ## w (t, a) | #define PROC_READ(w, t, a) proc_read_ ## w (t, a) | ||||
#define PROC_WRITE(w, t, a) proc_write_ ## w (t, a) | #define PROC_WRITE(w, t, a) proc_write_ ## w (t, a) | ||||
#endif | #endif | ||||
void | void | ||||
proc_set_traced(struct proc *p, bool stop) | proc_set_traced(struct proc *p, bool stop) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 191 Lines • ▼ Show 20 Lines | default: | ||||
} | } | ||||
/* OK */ | /* OK */ | ||||
break; | break; | ||||
} | } | ||||
/* Keep this process around until we finish this request. */ | /* Keep this process around until we finish this request. */ | ||||
_PHOLD(p); | _PHOLD(p); | ||||
Not Done Inline ActionsI do not understand this. proc_read/write_regset_alloc_iov() call malloc(M_WAITOK), while the process lock is owned. kib: I do not understand this. proc_read/write_regset_alloc_iov() call malloc(M_WAITOK), while the… | |||||
#ifdef FIX_SSTEP | #ifdef FIX_SSTEP | ||||
/* | /* | ||||
* Single step fixup ala procfs | * Single step fixup ala procfs | ||||
*/ | */ | ||||
FIX_SSTEP(td2); | FIX_SSTEP(td2); | ||||
#endif | #endif | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 438 Lines • ▼ Show 20 Lines | case PT_SETDBREGS: | ||||
td2->td_dbgflags |= TDB_USERWR; | td2->td_dbgflags |= TDB_USERWR; | ||||
error = PROC_WRITE(dbregs, td2, addr); | error = PROC_WRITE(dbregs, td2, addr); | ||||
break; | break; | ||||
case PT_GETDBREGS: | case PT_GETDBREGS: | ||||
CTR2(KTR_PTRACE, "PT_GETDBREGS: tid %d (pid %d)", td2->td_tid, | CTR2(KTR_PTRACE, "PT_GETDBREGS: tid %d (pid %d)", td2->td_tid, | ||||
p->p_pid); | p->p_pid); | ||||
error = PROC_READ(dbregs, td2, addr); | error = PROC_READ(dbregs, td2, addr); | ||||
break; | |||||
case PT_SETREGSET: | |||||
CTR2(KTR_PTRACE, "PT_SETREGSET: tid %d (pid %d)", td2->td_tid, | |||||
p->p_pid); | |||||
error = PROC_WRITE_REGSET(regset, td2, data, addr); | |||||
break; | |||||
case PT_GETREGSET: | |||||
CTR2(KTR_PTRACE, "PT_GETREGSET: tid %d (pid %d)", td2->td_tid, | |||||
p->p_pid); | |||||
error = PROC_READ_REGSET(regset, td2, data, addr); | |||||
break; | break; | ||||
case PT_LWPINFO: | case PT_LWPINFO: | ||||
if (data <= 0 || | if (data <= 0 || | ||||
#ifdef COMPAT_FREEBSD32 | #ifdef COMPAT_FREEBSD32 | ||||
(!wrap32 && data > sizeof(*pl)) || | (!wrap32 && data > sizeof(*pl)) || | ||||
(wrap32 && data > sizeof(*pl32))) { | (wrap32 && data > sizeof(*pl32))) { | ||||
#else | #else | ||||
▲ Show 20 Lines • Show All 168 Lines • Show Last 20 Lines |
We may consider in the future removing this check and always passing down 'vec->iov_len' as the size. This would let us grow an existing register set while still supporting the old size for compatibility. (Examples to consider here are fs_base/gs_base which Linux added to NT_PRSTATUS, or if we wanted to expand NT_PRSTATUS on arm64 to include the TLS register.) In practice I think we will just add new regsets for missing registers instead, but it's a thought. (fs_base and gs_base are kind of odd as we have ptrace ops for them today, but we don't include them in core dumps yet)