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