Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/compat/linux/linux_ptrace.c
39507 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2017 Edward Tomasz Napierala <[email protected]>
5
*
6
* This software was developed by SRI International and the University of
7
* Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
8
* ("CTSRD"), as part of the DARPA CRASH research programme.
9
*
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
12
* are met:
13
* 1. Redistributions of source code must retain the above copyright
14
* notice, this list of conditions and the following disclaimer.
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
*
19
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
* SUCH DAMAGE.
30
*/
31
32
#include <sys/param.h>
33
#include <sys/lock.h>
34
#include <sys/proc.h>
35
#include <sys/ptrace.h>
36
#include <sys/sx.h>
37
#include <sys/syscallsubr.h>
38
39
#include <machine/../linux/linux.h>
40
#include <machine/../linux/linux_proto.h>
41
#include <compat/linux/linux_emul.h>
42
#include <compat/linux/linux_errno.h>
43
#include <compat/linux/linux_misc.h>
44
#include <compat/linux/linux_signal.h>
45
#include <compat/linux/linux_util.h>
46
47
#define LINUX_PTRACE_TRACEME 0
48
#define LINUX_PTRACE_PEEKTEXT 1
49
#define LINUX_PTRACE_PEEKDATA 2
50
#define LINUX_PTRACE_PEEKUSER 3
51
#define LINUX_PTRACE_POKETEXT 4
52
#define LINUX_PTRACE_POKEDATA 5
53
#define LINUX_PTRACE_POKEUSER 6
54
#define LINUX_PTRACE_CONT 7
55
#define LINUX_PTRACE_KILL 8
56
#define LINUX_PTRACE_SINGLESTEP 9
57
#define LINUX_PTRACE_GETREGS 12
58
#define LINUX_PTRACE_SETREGS 13
59
#define LINUX_PTRACE_GETFPREGS 14
60
#define LINUX_PTRACE_SETFPREGS 15
61
#define LINUX_PTRACE_ATTACH 16
62
#define LINUX_PTRACE_DETACH 17
63
#define LINUX_PTRACE_SYSCALL 24
64
#define LINUX_PTRACE_SETOPTIONS 0x4200
65
#define LINUX_PTRACE_GETEVENTMSG 0x4201
66
#define LINUX_PTRACE_GETSIGINFO 0x4202
67
#define LINUX_PTRACE_GETREGSET 0x4204
68
#define LINUX_PTRACE_SEIZE 0x4206
69
#define LINUX_PTRACE_GET_SYSCALL_INFO 0x420e
70
71
#define LINUX_PTRACE_EVENT_EXEC 4
72
#define LINUX_PTRACE_EVENT_EXIT 6
73
74
#define LINUX_PTRACE_O_TRACESYSGOOD 1
75
#define LINUX_PTRACE_O_TRACEFORK 2
76
#define LINUX_PTRACE_O_TRACEVFORK 4
77
#define LINUX_PTRACE_O_TRACECLONE 8
78
#define LINUX_PTRACE_O_TRACEEXEC 16
79
#define LINUX_PTRACE_O_TRACEVFORKDONE 32
80
#define LINUX_PTRACE_O_TRACEEXIT 64
81
#define LINUX_PTRACE_O_TRACESECCOMP 128
82
#define LINUX_PTRACE_O_EXITKILL 1048576
83
#define LINUX_PTRACE_O_SUSPEND_SECCOMP 2097152
84
85
#define LINUX_NT_PRSTATUS 0x1
86
#define LINUX_NT_PRFPREG 0x2
87
#define LINUX_NT_X86_XSTATE 0x202
88
89
#define LINUX_PTRACE_O_MASK (LINUX_PTRACE_O_TRACESYSGOOD | \
90
LINUX_PTRACE_O_TRACEFORK | LINUX_PTRACE_O_TRACEVFORK | \
91
LINUX_PTRACE_O_TRACECLONE | LINUX_PTRACE_O_TRACEEXEC | \
92
LINUX_PTRACE_O_TRACEVFORKDONE | LINUX_PTRACE_O_TRACEEXIT | \
93
LINUX_PTRACE_O_TRACESECCOMP | LINUX_PTRACE_O_EXITKILL | \
94
LINUX_PTRACE_O_SUSPEND_SECCOMP)
95
96
#define LINUX_PTRACE_SYSCALL_INFO_NONE 0
97
#define LINUX_PTRACE_SYSCALL_INFO_ENTRY 1
98
#define LINUX_PTRACE_SYSCALL_INFO_EXIT 2
99
100
static int
101
map_signum(int lsig, int *bsigp)
102
{
103
int bsig;
104
105
if (lsig == 0) {
106
*bsigp = 0;
107
return (0);
108
}
109
110
if (lsig < 0 || lsig > LINUX_SIGRTMAX)
111
return (EINVAL);
112
113
bsig = linux_to_bsd_signal(lsig);
114
if (bsig == SIGSTOP)
115
bsig = 0;
116
117
*bsigp = bsig;
118
return (0);
119
}
120
121
int
122
linux_ptrace_status(struct thread *td, pid_t pid, int status)
123
{
124
struct ptrace_lwpinfo lwpinfo;
125
struct linux_pemuldata *pem;
126
register_t saved_retval;
127
int error;
128
129
saved_retval = td->td_retval[0];
130
error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo));
131
td->td_retval[0] = saved_retval;
132
if (error != 0) {
133
linux_msg(td, "PT_LWPINFO failed with error %d", error);
134
return (status);
135
}
136
137
pem = pem_find(td->td_proc);
138
KASSERT(pem != NULL, ("%s: proc emuldata not found.\n", __func__));
139
140
LINUX_PEM_SLOCK(pem);
141
if ((pem->ptrace_flags & LINUX_PTRACE_O_TRACESYSGOOD) &&
142
lwpinfo.pl_flags & PL_FLAG_SCE)
143
status |= (LINUX_SIGTRAP | 0x80) << 8;
144
if ((pem->ptrace_flags & LINUX_PTRACE_O_TRACESYSGOOD) &&
145
lwpinfo.pl_flags & PL_FLAG_SCX) {
146
if (lwpinfo.pl_flags & PL_FLAG_EXEC)
147
status |= (LINUX_SIGTRAP | LINUX_PTRACE_EVENT_EXEC << 8) << 8;
148
else
149
status |= (LINUX_SIGTRAP | 0x80) << 8;
150
}
151
if ((pem->ptrace_flags & LINUX_PTRACE_O_TRACEEXIT) &&
152
lwpinfo.pl_flags & PL_FLAG_EXITED)
153
status |= (LINUX_SIGTRAP | LINUX_PTRACE_EVENT_EXIT << 8) << 8;
154
LINUX_PEM_SUNLOCK(pem);
155
156
return (status);
157
}
158
159
static int
160
linux_ptrace_peek(struct thread *td, pid_t pid, void *addr, void *data)
161
{
162
int error;
163
164
error = kern_ptrace(td, PT_READ_I, pid, addr, 0);
165
if (error == 0)
166
error = copyout(td->td_retval, data, sizeof(l_int));
167
else if (error == ENOMEM)
168
error = EIO;
169
td->td_retval[0] = error;
170
171
return (error);
172
}
173
174
static int
175
linux_ptrace_setoptions(struct thread *td, pid_t pid, l_ulong data)
176
{
177
struct linux_pemuldata *pem;
178
int mask;
179
180
mask = 0;
181
182
if (data & ~LINUX_PTRACE_O_MASK) {
183
linux_msg(td, "unknown ptrace option %lx set; "
184
"returning EINVAL",
185
data & ~LINUX_PTRACE_O_MASK);
186
return (EINVAL);
187
}
188
189
pem = pem_find(td->td_proc);
190
KASSERT(pem != NULL, ("%s: proc emuldata not found.\n", __func__));
191
192
/*
193
* PTRACE_O_EXITKILL is ignored, we do that by default.
194
*/
195
196
LINUX_PEM_XLOCK(pem);
197
if (data & LINUX_PTRACE_O_TRACESYSGOOD) {
198
pem->ptrace_flags |= LINUX_PTRACE_O_TRACESYSGOOD;
199
} else {
200
pem->ptrace_flags &= ~LINUX_PTRACE_O_TRACESYSGOOD;
201
}
202
LINUX_PEM_XUNLOCK(pem);
203
204
if (data & LINUX_PTRACE_O_TRACEFORK)
205
mask |= PTRACE_FORK;
206
207
if (data & LINUX_PTRACE_O_TRACEVFORK)
208
mask |= PTRACE_VFORK;
209
210
if (data & LINUX_PTRACE_O_TRACECLONE)
211
mask |= PTRACE_VFORK;
212
213
if (data & LINUX_PTRACE_O_TRACEEXEC)
214
mask |= PTRACE_EXEC;
215
216
if (data & LINUX_PTRACE_O_TRACEVFORKDONE)
217
mask |= PTRACE_VFORK; /* XXX: Close enough? */
218
219
if (data & LINUX_PTRACE_O_TRACEEXIT) {
220
pem->ptrace_flags |= LINUX_PTRACE_O_TRACEEXIT;
221
} else {
222
pem->ptrace_flags &= ~LINUX_PTRACE_O_TRACEEXIT;
223
}
224
225
return (kern_ptrace(td, PT_SET_EVENT_MASK, pid, &mask, sizeof(mask)));
226
}
227
228
static int
229
linux_ptrace_geteventmsg(struct thread *td, pid_t pid, l_ulong data)
230
{
231
232
linux_msg(td, "PTRACE_GETEVENTMSG not implemented; returning EINVAL");
233
return (EINVAL);
234
}
235
236
static int
237
linux_ptrace_getsiginfo(struct thread *td, pid_t pid, l_ulong data)
238
{
239
struct ptrace_lwpinfo lwpinfo;
240
l_siginfo_t l_siginfo;
241
int error, sig;
242
243
error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo));
244
if (error != 0) {
245
linux_msg(td, "PT_LWPINFO failed with error %d", error);
246
return (error);
247
}
248
249
if ((lwpinfo.pl_flags & PL_FLAG_SI) == 0) {
250
error = EINVAL;
251
linux_msg(td, "no PL_FLAG_SI, returning %d", error);
252
return (error);
253
}
254
255
sig = bsd_to_linux_signal(lwpinfo.pl_siginfo.si_signo);
256
memset(&l_siginfo, 0, sizeof(l_siginfo));
257
siginfo_to_lsiginfo(&lwpinfo.pl_siginfo, &l_siginfo, sig);
258
error = copyout(&l_siginfo, (void *)data, sizeof(l_siginfo));
259
return (error);
260
}
261
262
static int
263
linux_ptrace_getregs(struct thread *td, pid_t pid, void *data)
264
{
265
struct reg b_reg;
266
struct linux_pt_regset l_regset;
267
int error;
268
269
error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0);
270
if (error != 0)
271
return (error);
272
273
bsd_to_linux_regset(&b_reg, &l_regset);
274
error = linux_ptrace_getregs_machdep(td, pid, &l_regset);
275
if (error != 0)
276
return (error);
277
278
error = copyout(&l_regset, (void *)data, sizeof(l_regset));
279
return (error);
280
}
281
282
static int
283
linux_ptrace_setregs(struct thread *td, pid_t pid, void *data)
284
{
285
struct reg b_reg;
286
struct linux_pt_regset l_regset;
287
int error;
288
289
error = copyin(data, &l_regset, sizeof(l_regset));
290
if (error != 0)
291
return (error);
292
linux_to_bsd_regset(&b_reg, &l_regset);
293
error = kern_ptrace(td, PT_SETREGS, pid, &b_reg, 0);
294
return (error);
295
}
296
297
static int
298
linux_ptrace_getregset_prstatus(struct thread *td, pid_t pid, l_ulong data)
299
{
300
struct reg b_reg;
301
struct linux_pt_regset l_regset;
302
struct iovec iov;
303
size_t len;
304
int error;
305
306
error = copyin((const void *)data, &iov, sizeof(iov));
307
if (error != 0) {
308
linux_msg(td, "copyin error %d", error);
309
return (error);
310
}
311
312
error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0);
313
if (error != 0)
314
return (error);
315
316
bsd_to_linux_regset(&b_reg, &l_regset);
317
error = linux_ptrace_getregs_machdep(td, pid, &l_regset);
318
if (error != 0)
319
return (error);
320
321
len = MIN(iov.iov_len, sizeof(l_regset));
322
error = copyout(&l_regset, (void *)iov.iov_base, len);
323
if (error != 0) {
324
linux_msg(td, "copyout error %d", error);
325
return (error);
326
}
327
328
iov.iov_len = len;
329
error = copyout(&iov, (void *)data, sizeof(iov));
330
if (error != 0) {
331
linux_msg(td, "iov copyout error %d", error);
332
return (error);
333
}
334
335
return (error);
336
}
337
338
static int
339
linux_ptrace_getregset(struct thread *td, pid_t pid, l_ulong addr, l_ulong data)
340
{
341
342
switch (addr) {
343
case LINUX_NT_PRSTATUS:
344
return (linux_ptrace_getregset_prstatus(td, pid, data));
345
case LINUX_NT_PRFPREG:
346
linux_msg(td, "PTRAGE_GETREGSET NT_PRFPREG not implemented; "
347
"returning EINVAL");
348
return (EINVAL);
349
case LINUX_NT_X86_XSTATE:
350
linux_msg(td, "PTRAGE_GETREGSET NT_X86_XSTATE not implemented; "
351
"returning EINVAL");
352
return (EINVAL);
353
default:
354
linux_msg(td, "PTRACE_GETREGSET request %#lx not implemented; "
355
"returning EINVAL", addr);
356
return (EINVAL);
357
}
358
}
359
360
static int
361
linux_ptrace_seize(struct thread *td, pid_t pid, l_ulong addr, l_ulong data)
362
{
363
364
linux_msg(td, "PTRACE_SEIZE not implemented; returning EINVAL");
365
return (EINVAL);
366
}
367
368
static int
369
linux_ptrace_get_syscall_info(struct thread *td, pid_t pid,
370
l_ulong len, l_ulong data)
371
{
372
struct ptrace_lwpinfo lwpinfo;
373
struct ptrace_sc_ret sr;
374
struct reg b_reg;
375
struct syscall_info si;
376
int error;
377
378
error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo));
379
if (error != 0) {
380
linux_msg(td, "PT_LWPINFO failed with error %d", error);
381
return (error);
382
}
383
384
memset(&si, 0, sizeof(si));
385
386
if (lwpinfo.pl_flags & PL_FLAG_SCE) {
387
si.op = LINUX_PTRACE_SYSCALL_INFO_ENTRY;
388
si.entry.nr = lwpinfo.pl_syscall_code;
389
error = kern_ptrace(td, PTLINUX_GET_SC_ARGS, pid,
390
si.entry.args, sizeof(si.entry.args));
391
if (error != 0) {
392
linux_msg(td,
393
"PT_LINUX_GET_SC_ARGS failed with error %d", error);
394
return (error);
395
}
396
} else if (lwpinfo.pl_flags & PL_FLAG_SCX) {
397
si.op = LINUX_PTRACE_SYSCALL_INFO_EXIT;
398
error = kern_ptrace(td, PT_GET_SC_RET, pid, &sr, sizeof(sr));
399
400
if (error != 0) {
401
linux_msg(td, "PT_GET_SC_RET failed with error %d",
402
error);
403
return (error);
404
}
405
406
if (sr.sr_error == 0) {
407
si.exit.rval = sr.sr_retval[0];
408
si.exit.is_error = 0;
409
} else if (sr.sr_error == EJUSTRETURN) {
410
/*
411
* EJUSTRETURN means the actual value to return
412
* has already been put into td_frame; instead
413
* of extracting it and trying to determine whether
414
* it's an error or not just bail out and let
415
* the ptracing process fall back to another method.
416
*/
417
si.op = LINUX_PTRACE_SYSCALL_INFO_NONE;
418
} else if (sr.sr_error == ERESTART) {
419
si.exit.rval = -LINUX_ERESTARTSYS;
420
si.exit.is_error = 1;
421
} else {
422
si.exit.rval = bsd_to_linux_errno(sr.sr_error);
423
si.exit.is_error = 1;
424
}
425
} else {
426
si.op = LINUX_PTRACE_SYSCALL_INFO_NONE;
427
}
428
429
error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0);
430
if (error != 0)
431
return (error);
432
433
linux_ptrace_get_syscall_info_machdep(&b_reg, &si);
434
435
len = MIN(len, sizeof(si));
436
error = copyout(&si, (void *)data, len);
437
if (error == 0)
438
td->td_retval[0] = sizeof(si);
439
440
return (error);
441
}
442
443
int
444
linux_ptrace(struct thread *td, struct linux_ptrace_args *uap)
445
{
446
void *addr;
447
pid_t pid;
448
int error, sig;
449
450
if (!allow_ptrace)
451
return (ENOSYS);
452
453
pid = (pid_t)uap->pid;
454
addr = (void *)uap->addr;
455
456
switch (uap->req) {
457
case LINUX_PTRACE_TRACEME:
458
error = kern_ptrace(td, PT_TRACE_ME, 0, 0, 0);
459
break;
460
case LINUX_PTRACE_PEEKTEXT:
461
case LINUX_PTRACE_PEEKDATA:
462
error = linux_ptrace_peek(td, pid, addr, (void *)uap->data);
463
if (error != 0)
464
goto out;
465
/*
466
* Linux expects this syscall to read 64 bits, not 32.
467
*/
468
error = linux_ptrace_peek(td, pid,
469
(void *)(uap->addr + 4), (void *)(uap->data + 4));
470
break;
471
case LINUX_PTRACE_PEEKUSER:
472
error = linux_ptrace_peekuser(td, pid, addr, (void *)uap->data);
473
break;
474
case LINUX_PTRACE_POKETEXT:
475
case LINUX_PTRACE_POKEDATA:
476
error = kern_ptrace(td, PT_WRITE_D, pid, addr, uap->data);
477
if (error != 0)
478
goto out;
479
/*
480
* Linux expects this syscall to write 64 bits, not 32.
481
*/
482
error = kern_ptrace(td, PT_WRITE_D, pid,
483
(void *)(uap->addr + 4), uap->data >> 32);
484
break;
485
case LINUX_PTRACE_POKEUSER:
486
error = linux_ptrace_pokeuser(td, pid, addr, (void *)uap->data);
487
break;
488
case LINUX_PTRACE_CONT:
489
error = map_signum(uap->data, &sig);
490
if (error != 0)
491
break;
492
error = kern_ptrace(td, PT_CONTINUE, pid, (void *)1, sig);
493
break;
494
case LINUX_PTRACE_KILL:
495
error = kern_ptrace(td, PT_KILL, pid, addr, uap->data);
496
break;
497
case LINUX_PTRACE_SINGLESTEP:
498
error = map_signum(uap->data, &sig);
499
if (error != 0)
500
break;
501
error = kern_ptrace(td, PT_STEP, pid, (void *)1, sig);
502
break;
503
case LINUX_PTRACE_GETREGS:
504
error = linux_ptrace_getregs(td, pid, (void *)uap->data);
505
break;
506
case LINUX_PTRACE_SETREGS:
507
error = linux_ptrace_setregs(td, pid, (void *)uap->data);
508
break;
509
case LINUX_PTRACE_ATTACH:
510
error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data);
511
break;
512
case LINUX_PTRACE_DETACH:
513
error = map_signum(uap->data, &sig);
514
if (error != 0)
515
break;
516
error = kern_ptrace(td, PT_DETACH, pid, (void *)1, sig);
517
break;
518
case LINUX_PTRACE_SYSCALL:
519
error = map_signum(uap->data, &sig);
520
if (error != 0)
521
break;
522
error = kern_ptrace(td, PT_SYSCALL, pid, (void *)1, sig);
523
break;
524
case LINUX_PTRACE_SETOPTIONS:
525
error = linux_ptrace_setoptions(td, pid, uap->data);
526
break;
527
case LINUX_PTRACE_GETEVENTMSG:
528
error = linux_ptrace_geteventmsg(td, pid, uap->data);
529
break;
530
case LINUX_PTRACE_GETSIGINFO:
531
error = linux_ptrace_getsiginfo(td, pid, uap->data);
532
break;
533
case LINUX_PTRACE_GETREGSET:
534
error = linux_ptrace_getregset(td, pid, uap->addr, uap->data);
535
break;
536
case LINUX_PTRACE_SEIZE:
537
error = linux_ptrace_seize(td, pid, uap->addr, uap->data);
538
break;
539
case LINUX_PTRACE_GET_SYSCALL_INFO:
540
error = linux_ptrace_get_syscall_info(td, pid, uap->addr, uap->data);
541
break;
542
default:
543
linux_msg(td, "ptrace(%ld, ...) not implemented; "
544
"returning EINVAL", uap->req);
545
error = EINVAL;
546
break;
547
}
548
549
out:
550
if (error == EBUSY)
551
error = ESRCH;
552
553
return (error);
554
}
555
556