xref: /freebsd/sys/amd64/amd64/ptrace_machdep.c (revision d0b2dbfa)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2011 Konstantin Belousov <kib@FreeBSD.org>
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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 #include <sys/cdefs.h>
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/elf.h>
34 #include <sys/lock.h>
35 #include <sys/malloc.h>
36 #include <sys/mutex.h>
37 #include <sys/proc.h>
38 #include <sys/ptrace.h>
39 #include <sys/reg.h>
40 #include <sys/sysent.h>
41 #include <vm/vm.h>
42 #include <vm/pmap.h>
43 #include <machine/md_var.h>
44 #include <machine/pcb.h>
45 #include <machine/frame.h>
46 #include <machine/vmparam.h>
47 
48 #ifdef COMPAT_FREEBSD32
49 struct ptrace_xstate_info32 {
50 	uint32_t	xsave_mask1, xsave_mask2;
51 	uint32_t	xsave_len;
52 };
53 #endif
54 
55 static bool
56 get_segbases(struct regset *rs, struct thread *td, void *buf,
57     size_t *sizep)
58 {
59 	struct segbasereg *reg;
60 	struct pcb *pcb;
61 
62 	if (buf != NULL) {
63 		KASSERT(*sizep == sizeof(*reg), ("%s: invalid size", __func__));
64 		reg = buf;
65 
66 		pcb = td->td_pcb;
67 		if (td == curthread)
68 			update_pcb_bases(pcb);
69 		reg->r_fsbase = pcb->pcb_fsbase;
70 		reg->r_gsbase = pcb->pcb_gsbase;
71 	}
72 	*sizep = sizeof(*reg);
73 	return (true);
74 }
75 
76 static bool
77 set_segbases(struct regset *rs, struct thread *td, void *buf,
78     size_t size)
79 {
80 	struct segbasereg *reg;
81 	struct pcb *pcb;
82 
83 	KASSERT(size == sizeof(*reg), ("%s: invalid size", __func__));
84 	reg = buf;
85 
86 	pcb = td->td_pcb;
87 	set_pcb_flags(pcb, PCB_FULL_IRET);
88 	pcb->pcb_fsbase = reg->r_fsbase;
89 	td->td_frame->tf_fs = _ufssel;
90 	pcb->pcb_gsbase = reg->r_gsbase;
91 	td->td_frame->tf_gs = _ugssel;
92 
93 	return (true);
94 }
95 
96 static struct regset regset_segbases = {
97 	.note = NT_X86_SEGBASES,
98 	.size = sizeof(struct segbasereg),
99 	.get = get_segbases,
100 	.set = set_segbases,
101 };
102 ELF_REGSET(regset_segbases);
103 
104 #ifdef COMPAT_FREEBSD32
105 static bool
106 get_segbases32(struct regset *rs, struct thread *td, void *buf,
107     size_t *sizep)
108 {
109 	struct segbasereg32 *reg;
110 	struct pcb *pcb;
111 
112 	if (buf != NULL) {
113 		KASSERT(*sizep == sizeof(*reg), ("%s: invalid size", __func__));
114 		reg = buf;
115 
116 		pcb = td->td_pcb;
117 		if (td == curthread)
118 			update_pcb_bases(pcb);
119 		reg->r_fsbase = (uint32_t)pcb->pcb_fsbase;
120 		reg->r_gsbase = (uint32_t)pcb->pcb_gsbase;
121 	}
122 	*sizep = sizeof(*reg);
123 	return (true);
124 }
125 
126 static bool
127 set_segbases32(struct regset *rs, struct thread *td, void *buf,
128     size_t size)
129 {
130 	struct segbasereg32 *reg;
131 	struct pcb *pcb;
132 
133 	KASSERT(size == sizeof(*reg), ("%s: invalid size", __func__));
134 	reg = buf;
135 
136 	pcb = td->td_pcb;
137 	set_pcb_flags(pcb, PCB_FULL_IRET);
138 	pcb->pcb_fsbase = reg->r_fsbase;
139 	td->td_frame->tf_fs = _ufssel;
140 	pcb->pcb_gsbase = reg->r_gsbase;
141 	td->td_frame->tf_gs = _ugssel;
142 
143 	return (true);
144 }
145 
146 static struct regset regset_segbases32 = {
147 	.note = NT_X86_SEGBASES,
148 	.size = sizeof(struct segbasereg32),
149 	.get = get_segbases32,
150 	.set = set_segbases32,
151 };
152 ELF32_REGSET(regset_segbases32);
153 #endif
154 
155 static int
156 cpu_ptrace_xstate(struct thread *td, int req, void *addr, int data)
157 {
158 	struct ptrace_xstate_info info;
159 #ifdef COMPAT_FREEBSD32
160 	struct ptrace_xstate_info32 info32;
161 #endif
162 	char *savefpu;
163 	int error;
164 
165 	if (!use_xsave)
166 		return (EOPNOTSUPP);
167 
168 	switch (req) {
169 	case PT_GETXSTATE_OLD:
170 		fpugetregs(td);
171 		savefpu = (char *)(get_pcb_user_save_td(td) + 1);
172 		error = copyout(savefpu, addr,
173 		    cpu_max_ext_state_size - sizeof(struct savefpu));
174 		break;
175 
176 	case PT_SETXSTATE_OLD:
177 		if (data > cpu_max_ext_state_size - sizeof(struct savefpu)) {
178 			error = EINVAL;
179 			break;
180 		}
181 		savefpu = malloc(data, M_TEMP, M_WAITOK);
182 		error = copyin(addr, savefpu, data);
183 		if (error == 0) {
184 			fpugetregs(td);
185 			error = fpusetxstate(td, savefpu, data);
186 		}
187 		free(savefpu, M_TEMP);
188 		break;
189 
190 	case PT_GETXSTATE_INFO:
191 #ifdef COMPAT_FREEBSD32
192 		if (SV_CURPROC_FLAG(SV_ILP32)) {
193 			if (data != sizeof(info32)) {
194 				error = EINVAL;
195 			} else {
196 				info32.xsave_len = cpu_max_ext_state_size;
197 				info32.xsave_mask1 = xsave_mask;
198 				info32.xsave_mask2 = xsave_mask >> 32;
199 				error = copyout(&info32, addr, data);
200 			}
201 		} else
202 #endif
203 		{
204 			if (data != sizeof(info)) {
205 				error  = EINVAL;
206 			} else {
207 				bzero(&info, sizeof(info));
208 				info.xsave_len = cpu_max_ext_state_size;
209 				info.xsave_mask = xsave_mask;
210 				error = copyout(&info, addr, data);
211 			}
212 		}
213 		break;
214 
215 	case PT_GETXSTATE:
216 		fpugetregs(td);
217 		savefpu = (char *)(get_pcb_user_save_td(td));
218 		error = copyout(savefpu, addr, cpu_max_ext_state_size);
219 		break;
220 
221 	case PT_SETXSTATE:
222 		if (data < sizeof(struct savefpu) ||
223 		    data > cpu_max_ext_state_size) {
224 			error = EINVAL;
225 			break;
226 		}
227 		savefpu = malloc(data, M_TEMP, M_WAITOK);
228 		error = copyin(addr, savefpu, data);
229 		if (error == 0)
230 			error = fpusetregs(td, (struct savefpu *)savefpu,
231 			    savefpu + sizeof(struct savefpu), data -
232 			    sizeof(struct savefpu));
233 		free(savefpu, M_TEMP);
234 		break;
235 
236 	default:
237 		error = EINVAL;
238 		break;
239 	}
240 
241 	return (error);
242 }
243 
244 static void
245 cpu_ptrace_setbase(struct thread *td, int req, register_t r)
246 {
247 	struct pcb *pcb;
248 
249 	pcb = td->td_pcb;
250 	set_pcb_flags(pcb, PCB_FULL_IRET);
251 	if (req == PT_SETFSBASE) {
252 		pcb->pcb_fsbase = r;
253 		td->td_frame->tf_fs = _ufssel;
254 	} else {
255 		pcb->pcb_gsbase = r;
256 		td->td_frame->tf_gs = _ugssel;
257 	}
258 }
259 
260 #ifdef COMPAT_FREEBSD32
261 #define PT_I386_GETXMMREGS	(PT_FIRSTMACH + 0)
262 #define PT_I386_SETXMMREGS	(PT_FIRSTMACH + 1)
263 
264 static int
265 cpu32_ptrace(struct thread *td, int req, void *addr, int data)
266 {
267 	struct savefpu *fpstate;
268 	struct pcb *pcb;
269 	uint32_t r;
270 	int error;
271 
272 	switch (req) {
273 	case PT_I386_GETXMMREGS:
274 		fpugetregs(td);
275 		error = copyout(get_pcb_user_save_td(td), addr,
276 		    sizeof(*fpstate));
277 		break;
278 
279 	case PT_I386_SETXMMREGS:
280 		fpugetregs(td);
281 		fpstate = get_pcb_user_save_td(td);
282 		error = copyin(addr, fpstate, sizeof(*fpstate));
283 		fpstate->sv_env.en_mxcsr &= cpu_mxcsr_mask;
284 		break;
285 
286 	case PT_GETXSTATE_OLD:
287 	case PT_SETXSTATE_OLD:
288 	case PT_GETXSTATE_INFO:
289 	case PT_GETXSTATE:
290 	case PT_SETXSTATE:
291 		error = cpu_ptrace_xstate(td, req, addr, data);
292 		break;
293 
294 	case PT_GETFSBASE:
295 	case PT_GETGSBASE:
296 		if (!SV_PROC_FLAG(td->td_proc, SV_ILP32)) {
297 			error = EINVAL;
298 			break;
299 		}
300 		pcb = td->td_pcb;
301 		if (td == curthread)
302 			update_pcb_bases(pcb);
303 		r = req == PT_GETFSBASE ? pcb->pcb_fsbase : pcb->pcb_gsbase;
304 		error = copyout(&r, addr, sizeof(r));
305 		break;
306 
307 	case PT_SETFSBASE:
308 	case PT_SETGSBASE:
309 		if (!SV_PROC_FLAG(td->td_proc, SV_ILP32)) {
310 			error = EINVAL;
311 			break;
312 		}
313 		error = copyin(addr, &r, sizeof(r));
314 		if (error != 0)
315 			break;
316 		cpu_ptrace_setbase(td, req, r);
317 		break;
318 
319 	default:
320 		error = EINVAL;
321 		break;
322 	}
323 
324 	return (error);
325 }
326 #endif
327 
328 int
329 cpu_ptrace(struct thread *td, int req, void *addr, int data)
330 {
331 	register_t *r, rv;
332 	struct pcb *pcb;
333 	int error;
334 
335 #ifdef COMPAT_FREEBSD32
336 	if (SV_CURPROC_FLAG(SV_ILP32))
337 		return (cpu32_ptrace(td, req, addr, data));
338 #endif
339 
340 	/* Support old values of PT_GETXSTATE_OLD and PT_SETXSTATE_OLD. */
341 	if (req == PT_FIRSTMACH + 0)
342 		req = PT_GETXSTATE_OLD;
343 	if (req == PT_FIRSTMACH + 1)
344 		req = PT_SETXSTATE_OLD;
345 
346 	switch (req) {
347 	case PT_GETXSTATE_OLD:
348 	case PT_SETXSTATE_OLD:
349 	case PT_GETXSTATE_INFO:
350 	case PT_GETXSTATE:
351 	case PT_SETXSTATE:
352 		error = cpu_ptrace_xstate(td, req, addr, data);
353 		break;
354 
355 	case PT_GETFSBASE:
356 	case PT_GETGSBASE:
357 		pcb = td->td_pcb;
358 		if (td == curthread)
359 			update_pcb_bases(pcb);
360 		r = req == PT_GETFSBASE ? &pcb->pcb_fsbase : &pcb->pcb_gsbase;
361 		error = copyout(r, addr, sizeof(*r));
362 		break;
363 
364 	case PT_SETFSBASE:
365 	case PT_SETGSBASE:
366 		error = copyin(addr, &rv, sizeof(rv));
367 		if (error != 0)
368 			break;
369 		if (rv >= td->td_proc->p_sysent->sv_maxuser) {
370 			error = EINVAL;
371 			break;
372 		}
373 		cpu_ptrace_setbase(td, req, rv);
374 		break;
375 
376 	default:
377 		error = EINVAL;
378 		break;
379 	}
380 
381 	return (error);
382 }
383 
384 int
385 ptrace_set_pc(struct thread *td, unsigned long addr)
386 {
387 
388 	td->td_frame->tf_rip = addr;
389 	set_pcb_flags(td->td_pcb, PCB_FULL_IRET);
390 	return (0);
391 }
392 
393 int
394 ptrace_single_step(struct thread *td)
395 {
396 
397 	PROC_LOCK_ASSERT(td->td_proc, MA_OWNED);
398 	if ((td->td_frame->tf_rflags & PSL_T) == 0) {
399 		td->td_frame->tf_rflags |= PSL_T;
400 		td->td_dbgflags |= TDB_STEP;
401 	}
402 	return (0);
403 }
404 
405 int
406 ptrace_clear_single_step(struct thread *td)
407 {
408 
409 	PROC_LOCK_ASSERT(td->td_proc, MA_OWNED);
410 	td->td_frame->tf_rflags &= ~PSL_T;
411 	td->td_dbgflags &= ~TDB_STEP;
412 	return (0);
413 }
414