xref: /netbsd-src/sys/kern/sys_process_lwpstatus.c (revision 5b38d912c8c95969e4667aadbd354225a2eb73b0)
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
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
78 process_read_lwpstatus(struct lwp *l, struct ptrace_lwpstatus *pls)
79 {
80 
81 	ptrace_read_lwpstatus(l, pls);
82 }
83 
84 int
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
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
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
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
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
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
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
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