diff --git a/sys/arch/amd64/amd64/process_machdep.c b/sys/arch/amd64/amd64/process_machdep.c index 58e215187d30..e5e0e10a1d78 100644 --- a/sys/arch/amd64/amd64/process_machdep.c +++ b/sys/arch/amd64/amd64/process_machdep.c @@ -283,12 +283,28 @@ process_set_pc(struct lwp *l, void *addr) } #ifdef __HAVE_PTRACE_MACHDEP +static int +process_machdep_read_tlsbase(struct lwp *l, __greg_t *reg) +{ + + *reg = (__greg_t)(uintptr_t)l->l_private; + + return 0; +} + static int process_machdep_read_xstate(struct lwp *l, struct xstate *regs) { return process_read_xstate(l, regs); } +static int +process_machdep_write_tlsbase(struct lwp *l, const __greg_t *reg) +{ + + return lwp_setprivate(l, (void *)(uintptr_t)*reg); +} + static int process_machdep_write_xstate(struct lwp *l, const struct xstate *regs) { @@ -370,6 +386,31 @@ ptrace_machdep_dorequest( MODULE_HOOK_CALL(netbsd32_process_doxmmregs_hook, (l, lt, addr, write), EINVAL, error); return error; + + case PT_SETTLSBASE: + write = 1; + + /* FALLTHROUGH */ + case PT_GETTLSBASE: + /* write = 0 done above. */ + if (!process_machdep_validtlsbase(lt->l_proc)) + return EINVAL; + error = proc_vmspace_getref(l->l_proc, &vm); + if (error) { + return error; + } + iov.iov_base = addr; + iov.iov_len = sizeof(__greg_t); + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = 0; + uio.uio_resid = sizeof(__greg_t); + uio.uio_rw = write ? UIO_WRITE : UIO_READ; + uio.uio_vmspace = vm; + error = process_machdep_dotlsbase(l, lt, &uio); + uvmspace_free(vm); + return error; + } #ifdef DIAGNOSTIC @@ -380,9 +421,54 @@ ptrace_machdep_dorequest( } /* - * The following functions are used by both ptrace(2) and procfs. + * The following functions are used by ptrace(2). */ +int +process_machdep_dotlsbase(struct lwp *curl, struct lwp *l, struct uio *uio) + /* curl: tracer */ + /* l: traced */ +{ + int error; + __greg_t r; + char *kv; + int kl; + + kl = sizeof(r); + kv = (char *) &r; + + kv += uio->uio_offset; + kl -= uio->uio_offset; + if (kl > uio->uio_resid) + kl = uio->uio_resid; + + if (kl < 0) + error = EINVAL; + else + error = process_machdep_read_tlsbase(l, &r); + if (error == 0) + error = uiomove(kv, kl, uio); + if (error == 0 && uio->uio_rw == UIO_WRITE) { + if (l->l_proc->p_stat != SSTOP) + error = EBUSY; + else + error = process_machdep_write_tlsbase(l, &r); + } + + uio->uio_offset = 0; + return error; +} + +int +process_machdep_validtlsbase(struct proc *p) +{ + + if (p->p_flag & PK_SYSTEM) + return 0; + + return 1; +} + int process_machdep_doxstate(struct lwp *curl, struct lwp *l, struct uio *uio) /* curl: tracer */ diff --git a/sys/arch/amd64/include/ptrace.h b/sys/arch/amd64/include/ptrace.h index 8820ed8f9061..73f3a11d486a 100644 --- a/sys/arch/amd64/include/ptrace.h +++ b/sys/arch/amd64/include/ptrace.h @@ -54,6 +54,8 @@ #define PT_GETXMMREGS (PT_FIRSTMACH + 11) #define PT_SETXMMREGS (PT_FIRSTMACH + 12) #endif +#define PT_GETTLSBASE (PT_FIRSTMACH + 13) +#define PT_SETTLSBASE (PT_FIRSTMACH + 14) /* We have machine-dependent process tracing needs. */ #define __HAVE_PTRACE_MACHDEP @@ -69,7 +71,11 @@ "PT_SETSTEP", \ "PT_CLEARSTEP", \ "PT_GETXSTATE", \ - "PT_SETXSTATE" + "PT_SETXSTATE", \ + "PT_GETXMMREGS", \ + "PT_SETXMMREGS", \ + "PT_GETTLSBASE", \ + "PT_SETTLSBASE" #include #define PTRACE_REG_PC(r) (r)->regs[_REG_RIP] @@ -94,11 +100,16 @@ case PT_GETXSTATE: \ case PT_SETXSTATE: \ case PT_GETXMMREGS: \ - case PT_SETXMMREGS: + case PT_SETXMMREGS: \ + case PT_GETTLSBASE: \ + case PT_SETTLSBASE: int process_machdep_doxstate(struct lwp *, struct lwp *, struct uio *); int process_machdep_validfpu(struct proc *); +int process_machdep_dotlsbase(struct lwp *, struct lwp *, struct uio *); +int process_machdep_validtlsbase(struct proc *); + #include MODULE_HOOK(netbsd32_process_doxmmregs_hook, int, (struct lwp *, struct lwp *, void *, bool)); diff --git a/tests/lib/libc/sys/t_ptrace_wait.c b/tests/lib/libc/sys/t_ptrace_wait.c index 76088a3e60d8..1a727a87911e 100644 --- a/tests/lib/libc/sys/t_ptrace_wait.c +++ b/tests/lib/libc/sys/t_ptrace_wait.c @@ -4379,7 +4379,7 @@ BYTES_TRANSFER_EOF(bytes_transfer_eof_piod_write_d, "PIOD_WRITE_D") /// ---------------------------------------------------------------------------- -#if defined(HAVE_GPREGS) || defined(HAVE_FPREGS) +#if defined(HAVE_GPREGS) || defined(HAVE_FPREGS) || defined(HAVE_TLSBASE) static void access_regs(const char *regset, const char *aux) { @@ -4396,6 +4396,9 @@ access_regs(const char *regset, const char *aux) #if defined(HAVE_FPREGS) struct fpreg fpr; #endif +#if defined(HAVE_TLSBASE) + register_t r; +#endif #if !defined(HAVE_GPREGS) if (strcmp(regset, "regs") == 0) @@ -4407,6 +4410,11 @@ access_regs(const char *regset, const char *aux) atf_tc_fail("Impossible test scenario!"); #endif +#if !defined(HAVE_TLSBASE) + if (strcmp(regset, "tlsbase") == 0) + atf_tc_fail("Impossible test scenario!"); +#endif + DPRINTF("Before forking process PID=%d\n", getpid()); SYSCALL_REQUIRE((child = fork()) != -1); if (child == 0) { @@ -4468,6 +4476,21 @@ access_regs(const char *regset, const char *aux) } #endif +#if defined(HAVE_TLSBASE) + if (strcmp(regset, "tlsbase") == 0) { + DPRINTF("Call GETTLSBASE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_GETTLSBASE, child, &r, 0) != -1); + + DPRINTF("Retrieved TLSBASE register: %" PRIxREGISTER "\n", r); + + if (strcmp(aux, "settlsbase") == 0) { + DPRINTF("Call SETTLSBASE for the child\n"); + SYSCALL_REQUIRE( + ptrace(PT_SETTLSBASE, child, &r, 0) != -1); + } + } +#endif + DPRINTF("Before resuming the child process where it left off and " "without signal to be sent\n"); SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); @@ -4508,6 +4531,10 @@ ACCESS_REGS(access_regs6, "regs", "setregs") ACCESS_REGS(access_fpregs1, "fpregs", "getfpregs") ACCESS_REGS(access_fpregs2, "fpregs", "setfpregs") #endif +#if defined(HAVE_TLSBASE) +ACCESS_REGS(access_tlsbase1, "tlsbase", "gettlsbase") +ACCESS_REGS(access_tlsbase2, "tlsbase", "settlsbase") +#endif /// ---------------------------------------------------------------------------- @@ -8169,6 +8196,9 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC_HAVE_FPREGS(tp, access_fpregs1); ATF_TP_ADD_TC_HAVE_FPREGS(tp, access_fpregs2); + ATF_TP_ADD_TC_HAVE_TLSBASE(tp, access_tlsbase1); + ATF_TP_ADD_TC_HAVE_TLSBASE(tp, access_tlsbase2); + ATF_TP_ADD_TC_PT_STEP(tp, step1); ATF_TP_ADD_TC_PT_STEP(tp, step2); ATF_TP_ADD_TC_PT_STEP(tp, step3); diff --git a/tests/lib/libc/sys/t_ptrace_wait.h b/tests/lib/libc/sys/t_ptrace_wait.h index a840de3da27c..ecf5818901d7 100644 --- a/tests/lib/libc/sys/t_ptrace_wait.h +++ b/tests/lib/libc/sys/t_ptrace_wait.h @@ -207,6 +207,12 @@ do { \ #define HAVE_DBREGS #endif +/* Add guards for TLSBASE register */ +#if defined(PT_GETTLSBASE) \ + && defined(PT_SETTLSBASE) +#define HAVE_TLSBASE +#endif + /* * If waitid(2) returns because one or more processes have a state change to * report, 0 is returned. If an error is detected, a value of -1 is returned @@ -722,6 +728,12 @@ find_event_count(struct lwp_event_count list[], lwpid_t lwp, size_t max_lwps) #define ATF_TP_ADD_TC_HAVE_DBREGS(a,b) #endif +#if defined(HAVE_TLSBASE) +#define ATF_TP_ADD_TC_HAVE_TLSBASE(a,b) ATF_TP_ADD_TC(a,b) +#else +#define ATF_TP_ADD_TC_HAVE_TLSBASE(a,b) +#endif + #if defined(PT_STEP) #define ATF_TP_ADD_TC_PT_STEP(a,b) ATF_TP_ADD_TC(a,b) #else