aboutsummaryrefslogtreecommitdiffstats
path: root/exec
diff options
context:
space:
mode:
authorPo Lu2023-05-05 19:04:32 +0800
committerPo Lu2023-05-05 19:04:32 +0800
commit0fbe79727b07879cb4f0a5cb8d7288353c082bd0 (patch)
tree921d949e0403488deba0ae13cc55ef736d095e23 /exec
parent2ba6c5035c904426d564eac47381480158cbbb9e (diff)
downloademacs-0fbe79727b07879cb4f0a5cb8d7288353c082bd0.tar.gz
emacs-0fbe79727b07879cb4f0a5cb8d7288353c082bd0.zip
Fix execution of /proc/self/exe within child processes
* exec/exec.h (struct exec_tracee): New field `new_child'. Also, make `waiting_for_syscall' a bitfield. * exec/trace.c (PTRACE_GETEVENTMSG): New declaration. (MAX_TRACEES): Bump to 4096. (handle_clone_prepare): New function. (handle_clone): If required, set `new_child' and wait for a ptrace event describing the parent to arrive. (after_fork): Clear new field. (exec_waitpid): Upon a ptrace event describing a clone, create the child's tracee if it doesn't already exist. Otherwise, copy over the parent's cmdline and start running it.
Diffstat (limited to 'exec')
-rw-r--r--exec/exec.h6
-rw-r--r--exec/trace.c187
2 files changed, 154 insertions, 39 deletions
diff --git a/exec/exec.h b/exec/exec.h
index 625ad0bb219..8ee74d7ca8b 100644
--- a/exec/exec.h
+++ b/exec/exec.h
@@ -153,7 +153,11 @@ struct exec_tracee
153 153
154 /* Whether or not the tracee is currently waiting for a system call 154 /* Whether or not the tracee is currently waiting for a system call
155 to complete. */ 155 to complete. */
156 bool waiting_for_syscall; 156 bool waiting_for_syscall : 1;
157
158 /* Whether or not the tracee has been created but is not yet
159 processed by `handle_clone'. */
160 bool new_child : 1;
157 161
158#ifndef REENTRANT 162#ifndef REENTRANT
159 /* Name of the executable being run. */ 163 /* Name of the executable being run. */
diff --git a/exec/trace.c b/exec/trace.c
index b765b5cffa4..974df1dd5e1 100644
--- a/exec/trace.c
+++ b/exec/trace.c
@@ -50,6 +50,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
50#define SYS_SECCOMP 1 50#define SYS_SECCOMP 1
51#endif /* SYS_SECCOMP */ 51#endif /* SYS_SECCOMP */
52 52
53#ifndef PTRACE_GETEVENTMSG
54#define PTRACE_GETEVENTMSG 0x4201
55#endif /* PTRACE_GETEVENTMSG */
56
53 57
54 58
55/* Program tracing functions. 59/* Program tracing functions.
@@ -63,7 +67,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
63 67
64 68
65/* Number of tracees children are allowed to create. */ 69/* Number of tracees children are allowed to create. */
66#define MAX_TRACEES 1024 70#define MAX_TRACEES 4096
67 71
68#ifdef __aarch64__ 72#ifdef __aarch64__
69 73
@@ -381,23 +385,66 @@ remove_tracee (struct exec_tracee *tracee)
381 385
382/* Child process tracing. */ 386/* Child process tracing. */
383 387
384/* Handle the completion of a `clone' or `clone3' system call, 388/* Array of `struct exec_tracees' that they are allocated from. */
385 resulting in the creation of the process PID. Allocate a new 389static struct exec_tracee static_tracees[MAX_TRACEES];
386 tracee structure from a static area for the processes's pid.
387 390
388 Value is 0 upon success, 1 otherwise. */ 391/* Number of tracees currently allocated. */
392static int tracees;
389 393
390static int 394/* Return the `struct exec_tracee' corresponding to the specified
391handle_clone (pid_t pid) 395 PROCESS. */
396
397static struct exec_tracee *
398find_tracee (pid_t process)
392{ 399{
393 static struct exec_tracee static_tracees[MAX_TRACEES];
394 static int tracees;
395 struct exec_tracee *tracee; 400 struct exec_tracee *tracee;
401
402 for (tracee = tracing_processes; tracee; tracee = tracee->next)
403 {
404 if (tracee->pid == process)
405 return tracee;
406 }
407
408 return NULL;
409}
410
411/* Prepare to handle the completion of a `clone' system call.
412
413 If the new clone is not yet being traced, create a new tracee for
414 PARENT's child, copying over its current command line. Then, set
415 `new_child' in the new tracee. Otherwise, continue it until the
416 next syscall. */
417
418static void
419handle_clone_prepare (struct exec_tracee *parent)
420{
421#ifndef REENTRANT
396 long rc; 422 long rc;
397 int flags; 423 unsigned long pid;
424 struct exec_tracee *tracee;
398 425
399 /* Now allocate a new tracee, either from static_tracees or the free 426 rc = ptrace (PTRACE_GETEVENTMSG, parent->pid, NULL,
400 list. */ 427 &pid);
428 if (rc)
429 return;
430
431 /* See if the tracee already exists. */
432 tracee = find_tracee (pid);
433
434 if (tracee)
435 {
436 /* Continue the tracee. Record its command line, as that has
437 not yet been done. */
438
439 assert (tracee->new_child);
440 tracee->new_child = false;
441 tracee->exec_file = NULL;
442 ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0);
443
444 if (parent->exec_file)
445 tracee->exec_file = strdup (parent->exec_file);
446 return;
447 }
401 448
402 if (free_tracees) 449 if (free_tracees)
403 { 450 {
@@ -410,13 +457,75 @@ handle_clone (pid_t pid)
410 tracees++; 457 tracees++;
411 } 458 }
412 else 459 else
413 return 1; 460 return;
414 461
415 tracee->pid = pid; 462 tracee->pid = pid;
416 tracee->next = tracing_processes; 463 tracee->next = tracing_processes;
417 tracee->waiting_for_syscall = false; 464 tracee->waiting_for_syscall = false;
465 tracee->new_child = true;
466 tracee->exec_file = NULL;
418 tracing_processes = tracee; 467 tracing_processes = tracee;
419 468
469 /* Copy over the command line. */
470
471 if (parent->exec_file)
472 tracee->exec_file = strdup (parent->exec_file);
473#endif /* REENTRANT */
474}
475
476/* Handle the completion of a `clone' or `clone3' system call,
477 resulting in the creation of the process PID. If TRACEE is NULL,
478 allocate a new tracee structure from a static area for the
479 processes's pid, then set TRACEE->new_child to true and await the
480 parent's corresponding ptrace event to arrive; otherwise, just
481 clear TRACEE->new_child.
482
483 Value is 0 upon success, 2 if TRACEE should remain suspended until
484 the parent's ptrace-stop, and 1 otherwise. */
485
486static int
487handle_clone (struct exec_tracee *tracee, pid_t pid)
488{
489 long rc;
490 int flags, value;
491
492 /* Now allocate a new tracee, either from static_tracees or the free
493 list, if no tracee was supplied. */
494
495 value = 0;
496
497 if (!tracee)
498 {
499 if (free_tracees)
500 {
501 tracee = free_tracees;
502 free_tracees = free_tracees->next;
503 }
504 else if (tracees < MAX_TRACEES)
505 {
506 tracee = &static_tracees[tracees];
507 tracees++;
508 }
509 else
510 return 1;
511
512 tracee->pid = pid;
513 tracee->next = tracing_processes;
514 tracee->waiting_for_syscall = false;
515#ifndef REENTRANT
516 tracee->exec_file = NULL;
517#endif /* REENTRANT */
518 tracing_processes = tracee;
519 tracee->new_child = true;
520
521 /* Wait for the ptrace-stop to happen in the parent. */
522 value = 2;
523 }
524 else
525 /* Clear the flag saying that this is a newly created child
526 process. */
527 tracee->new_child = false;
528
420 /* Apply required options to the child, so that the kernel 529 /* Apply required options to the child, so that the kernel
421 automatically traces children and makes it easy to differentiate 530 automatically traces children and makes it easy to differentiate
422 between system call traps and other kinds of traps. */ 531 between system call traps and other kinds of traps. */
@@ -432,15 +541,18 @@ handle_clone (pid_t pid)
432 if (rc) 541 if (rc)
433 goto bail; 542 goto bail;
434 543
435 /* The new tracee is currently stopped. Continue it until the next 544 if (value != 2)
436 system call. */ 545 {
546 /* The new tracee is currently stopped. Continue it until the next
547 system call. */
437 548
438 rc = ptrace (PTRACE_SYSCALL, pid, 0, 0); 549 rc = ptrace (PTRACE_SYSCALL, pid, 0, 0);
439 550
440 if (rc) 551 if (rc)
441 goto bail; 552 goto bail;
553 }
442 554
443 return 0; 555 return value;
444 556
445 bail: 557 bail:
446 remove_tracee (tracee); 558 remove_tracee (tracee);
@@ -1148,6 +1260,7 @@ after_fork (pid_t pid)
1148 tracee->pid = pid; 1260 tracee->pid = pid;
1149 tracee->next = tracing_processes; 1261 tracee->next = tracing_processes;
1150 tracee->waiting_for_syscall = false; 1262 tracee->waiting_for_syscall = false;
1263 tracee->new_child = false;
1151#ifndef REENTRANT 1264#ifndef REENTRANT
1152 tracee->exec_file = NULL; 1265 tracee->exec_file = NULL;
1153#endif /* REENTRANT */ 1266#endif /* REENTRANT */
@@ -1155,23 +1268,6 @@ after_fork (pid_t pid)
1155 return 0; 1268 return 0;
1156} 1269}
1157 1270
1158/* Return the `struct exec_tracee' corresponding to the specified
1159 PROCESS. */
1160
1161static struct exec_tracee *
1162find_tracee (pid_t process)
1163{
1164 struct exec_tracee *tracee;
1165
1166 for (tracee = tracing_processes; tracee; tracee = tracee->next)
1167 {
1168 if (tracee->pid == process)
1169 return tracee;
1170 }
1171
1172 return NULL;
1173}
1174
1175/* Wait for a child process to exit, like `waitpid'. However, if a 1271/* Wait for a child process to exit, like `waitpid'. However, if a
1176 child stops to perform a system call, send it on its way and return 1272 child stops to perform a system call, send it on its way and return
1177 -1. OPTIONS must not contain WUNTRACED. */ 1273 -1. OPTIONS must not contain WUNTRACED. */
@@ -1199,12 +1295,12 @@ exec_waitpid (pid_t pid, int *wstatus, int options)
1199 { 1295 {
1200 tracee = find_tracee (pid); 1296 tracee = find_tracee (pid);
1201 1297
1202 if (!tracee) 1298 if (!tracee || tracee->new_child)
1203 { 1299 {
1204 if (WSTOPSIG (status) == SIGSTOP) 1300 if (WSTOPSIG (status) == SIGSTOP)
1205 /* A new process has been created and stopped. Record 1301 /* A new process has been created and stopped. Record
1206 it now. */ 1302 it now. */
1207 handle_clone (pid); 1303 handle_clone (tracee, pid);
1208 1304
1209 return -1; 1305 return -1;
1210 } 1306 }
@@ -1248,6 +1344,21 @@ exec_waitpid (pid_t pid, int *wstatus, int options)
1248 case SIGTRAP | (PTRACE_EVENT_FORK << 8): 1344 case SIGTRAP | (PTRACE_EVENT_FORK << 8):
1249 case SIGTRAP | (PTRACE_EVENT_VFORK << 8): 1345 case SIGTRAP | (PTRACE_EVENT_VFORK << 8):
1250 case SIGTRAP | (PTRACE_EVENT_CLONE << 8): 1346 case SIGTRAP | (PTRACE_EVENT_CLONE << 8):
1347
1348 /* Both PTRACE_EVENT_CLONE and SIGSTOP must arrive before a
1349 process is continued. Otherwise, its parent's cmdline
1350 cannot be obtained and propagated.
1351
1352 If the PID of the new process is currently not being
1353 traced, create a new tracee. Set `new_child' to true,
1354 and copy over the old command line in preparation for a
1355 SIGSTOP signal being delivered to it.
1356
1357 Otherwise, start the tracee running until the next
1358 syscall. */
1359
1360 handle_clone_prepare (tracee);
1361
1251 /* These events are handled by tracing SIGSTOP signals sent 1362 /* These events are handled by tracing SIGSTOP signals sent
1252 to unknown tracees. Make sure not to pass through 1363 to unknown tracees. Make sure not to pass through
1253 status, as there's no signal really being delivered. */ 1364 status, as there's no signal really being delivered. */