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