1 /*        $NetBSD: sys_process_lwpstatus.c,v 1.5 2025/01/11 19:42:04 christos Exp $       */
2 
3 /*-
4  * Copyright (c) 2019 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: sys_process_lwpstatus.c,v 1.5 2025/01/11 19:42:04 christos Exp $");
31 
32 #ifdef _KERNEL_OPT
33 #include "opt_ptrace.h"
34 #include "opt_ktrace.h"
35 #include "opt_pax.h"
36 #include "opt_compat_netbsd32.h"
37 #endif
38 
39 #if defined(__HAVE_COMPAT_NETBSD32) && !defined(COMPAT_NETBSD32) \
40     && !defined(_RUMPKERNEL)
41 #define COMPAT_NETBSD32
42 #endif
43 
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/errno.h>
47 #include <sys/lwp.h>
48 #include <sys/proc.h>
49 #include <sys/ptrace.h>
50 
51 #ifndef PTRACE_REGS_ALIGN
52 #define PTRACE_REGS_ALIGN /* nothing */
53 #endif
54 
55 void
ptrace_read_lwpstatus(struct lwp * l,struct ptrace_lwpstatus * pls)56 ptrace_read_lwpstatus(struct lwp *l, struct ptrace_lwpstatus *pls)
57 {
58 
59           pls->pl_lwpid = l->l_lid;
60           memcpy(&pls->pl_sigmask, &l->l_sigmask, sizeof(pls->pl_sigmask));
61           memcpy(&pls->pl_sigpend, &l->l_sigpend.sp_set, sizeof(pls->pl_sigpend));
62 
63           if (l->l_name == NULL)
64                     memset(&pls->pl_name, 0, PL_LNAMELEN);
65           else {
66                     KASSERT(strlen(l->l_name) < PL_LNAMELEN);
67                     strncpy(pls->pl_name, l->l_name, PL_LNAMELEN);
68           }
69 
70 #ifdef PTRACE_LWP_GETPRIVATE
71           pls->pl_private = (void *)(intptr_t)PTRACE_LWP_GETPRIVATE(l);
72 #else
73           pls->pl_private = l->l_private;
74 #endif
75 }
76 
77 void
process_read_lwpstatus(struct lwp * l,struct ptrace_lwpstatus * pls)78 process_read_lwpstatus(struct lwp *l, struct ptrace_lwpstatus *pls)
79 {
80 
81           ptrace_read_lwpstatus(l, pls);
82 }
83 
84 int
ptrace_update_lwp(struct proc * t,struct lwp ** lt,lwpid_t lid)85 ptrace_update_lwp(struct proc *t, struct lwp **lt, lwpid_t lid)
86 {
87           if (lid == 0 || lid == (*lt)->l_lid || t->p_nlwps == 1)
88                     return 0;
89 
90           mutex_enter(t->p_lock);
91           lwp_delref2(*lt);
92 
93           *lt = lwp_find(t, lid);
94           if (*lt == NULL) {
95                     mutex_exit(t->p_lock);
96                     return ESRCH;
97           }
98 
99           if ((*lt)->l_flag & LW_SYSTEM) {
100                     mutex_exit(t->p_lock);
101                     *lt = NULL;
102                     return EINVAL;
103           }
104 
105           lwp_addref(*lt);
106           mutex_exit(t->p_lock);
107 
108           return 0;
109 }
110 
111 int
process_validfpregs(struct lwp * l)112 process_validfpregs(struct lwp *l)
113 {
114 
115 #if defined(PT_FPREGS)
116           return (l->l_flag & LW_SYSTEM) == 0;
117 #else
118           return 0;
119 #endif
120 }
121 
122 int
process_validregs(struct lwp * l)123 process_validregs(struct lwp *l)
124 {
125 
126 #if defined(PT_REGS)
127           return (l->l_flag & LW_SYSTEM) == 0;
128 #else
129           return 0;
130 #endif
131 }
132 
133 int
process_validdbregs(struct lwp * l)134 process_validdbregs(struct lwp *l)
135 {
136 
137 #if defined(PT_DBREGS)
138           return (l->l_flag & LW_SYSTEM) == 0;
139 #else
140           return 0;
141 #endif
142 }
143 
144 #ifdef PT_REGISTERS
145 static int
proc_regio(struct lwp * l,struct uio * uio,size_t ks,ptrace_regrfunc_t r,ptrace_regwfunc_t w)146 proc_regio(struct lwp *l, struct uio *uio, size_t ks, ptrace_regrfunc_t r,
147     ptrace_regwfunc_t w)
148 {
149           char buf[1024] PTRACE_REGS_ALIGN;
150           int error;
151           char *kv;
152           size_t kl;
153 
154           if (ks > sizeof(buf))
155                     return E2BIG;
156 
157           if (uio->uio_offset < 0 || uio->uio_offset > (off_t)ks)
158                     return EINVAL;
159 
160           kv = buf + uio->uio_offset;
161           kl = ks - uio->uio_offset;
162 
163           if (kl > uio->uio_resid)
164                     kl = uio->uio_resid;
165 
166           error = (*r)(l, buf, &ks);
167           if (error == 0)
168                     error = uiomove(kv, kl, uio);
169           if (error == 0 && uio->uio_rw == UIO_WRITE) {
170                     if (l->l_stat != LSSTOP)
171                               error = EBUSY;
172                     else
173                               error = (*w)(l, buf, ks);
174           }
175 
176           uio->uio_offset = 0;
177           return error;
178 }
179 #endif
180 
181 int
process_doregs(struct lwp * curl,struct lwp * l,struct uio * uio)182 process_doregs(struct lwp *curl /*tracer*/,
183     struct lwp *l /*traced*/,
184     struct uio *uio)
185 {
186 #if defined(PT_REGS)
187           size_t s;
188           ptrace_regrfunc_t r;
189           ptrace_regwfunc_t w;
190 
191 #ifdef COMPAT_NETBSD32
192           const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
193 
194           if (__predict_false(pk32)) {
195                     if ((l->l_proc->p_flag & PK_32) == 0) {
196                               // 32 bit tracer can't trace 64 bit process
197                               return EINVAL;
198                     }
199                     s = sizeof(process_reg32);
200                     r = __FPTRCAST(ptrace_regrfunc_t, process_read_regs32);
201                     w = __FPTRCAST(ptrace_regwfunc_t, process_write_regs32);
202           } else
203 #endif
204           {
205                     s = sizeof(struct reg);
206                     r = __FPTRCAST(ptrace_regrfunc_t, process_read_regs);
207                     w = __FPTRCAST(ptrace_regwfunc_t, process_write_regs);
208           }
209           return proc_regio(l, uio, s, r, w);
210 #else
211           return EINVAL;
212 #endif
213 }
214 
215 int
process_dofpregs(struct lwp * curl,struct lwp * l,struct uio * uio)216 process_dofpregs(struct lwp *curl /*tracer*/,
217     struct lwp *l /*traced*/,
218     struct uio *uio)
219 {
220 #if defined(PT_FPREGS)
221           size_t s;
222           ptrace_regrfunc_t r;
223           ptrace_regwfunc_t w;
224 
225 #ifdef COMPAT_NETBSD32
226           const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
227 
228           if (__predict_false(pk32)) {
229                     if ((l->l_proc->p_flag & PK_32) == 0) {
230                               // 32 bit tracer can't trace 64 bit process
231                               return EINVAL;
232                     }
233                     s = sizeof(process_fpreg32);
234                     r = (ptrace_regrfunc_t)process_read_fpregs32;
235                     w = (ptrace_regwfunc_t)process_write_fpregs32;
236           } else
237 #endif
238           {
239                     s = sizeof(struct fpreg);
240                     r = (ptrace_regrfunc_t)process_read_fpregs;
241                     w = (ptrace_regwfunc_t)process_write_fpregs;
242           }
243           return proc_regio(l, uio, s, r, w);
244 #else
245           return EINVAL;
246 #endif
247 }
248 
249 
250 int
process_dodbregs(struct lwp * curl,struct lwp * l,struct uio * uio)251 process_dodbregs(struct lwp *curl /*tracer*/,
252     struct lwp *l /*traced*/,
253     struct uio *uio)
254 {
255 #if defined(PT_DBREGS)
256           size_t s;
257           ptrace_regrfunc_t r;
258           ptrace_regwfunc_t w;
259 
260 #ifdef COMPAT_NETBSD32
261           const bool pk32 = (curl->l_proc->p_flag & PK_32) != 0;
262 
263           if (__predict_false(pk32)) {
264                     if ((l->l_proc->p_flag & PK_32) == 0) {
265                               // 32 bit tracer can't trace 64 bit process
266                               return EINVAL;
267                     }
268                     s = sizeof(process_dbreg32);
269                     r = (ptrace_regrfunc_t)process_read_dbregs32;
270                     w = (ptrace_regwfunc_t)process_write_dbregs32;
271           } else
272 #endif
273           {
274                     s = sizeof(struct dbreg);
275                     r = (ptrace_regrfunc_t)process_read_dbregs;
276                     w = (ptrace_regwfunc_t)process_write_dbregs;
277           }
278           return proc_regio(l, uio, s, r, w);
279 #else
280           return EINVAL;
281 #endif
282 }
283