File: exec_darwin.c

package info (click to toggle)
delve 1.24.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 14,092 kB
  • sloc: ansic: 111,943; sh: 169; asm: 141; makefile: 43; python: 23
file content (114 lines) | stat: -rw-r--r-- 2,381 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//+build darwin,macnative

#include "exec_darwin.h"
#include "stdio.h"

extern char** environ;

int
close_exec_pipe(int fd[2]) {
	if (pipe(fd) < 0) return -1;
	if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0) return -1;
	if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) < 0) return -1;
	return 0;
}

int
fork_exec(char *argv0, char **argv, int size,
		char *wd,
		task_t *task,
		mach_port_t *port_set,
		mach_port_t *exception_port,
		mach_port_t *notification_port)
{
	// Since we're using mach exceptions instead of signals,
	// we need to coordinate between parent and child via pipes
	// to ensure that the parent has set the exception ports on
	// the child task before it execs.
	int fd[2];
	if (close_exec_pipe(fd) < 0) return -1;

	// Create another pipe to signal the parent on exec.
	int efd[2];
	if (close_exec_pipe(efd) < 0) return -1;

	kern_return_t kret;
	pid_t pid = fork();
	if (pid > 0) {
		// In parent.
		close(fd[0]);
		close(efd[1]);
		kret = acquire_mach_task(pid, task, port_set, exception_port, notification_port);
		if (kret != KERN_SUCCESS) return -1;

		char msg = 'c';
		write(fd[1], &msg, 1);
		close(fd[1]);

		char w;
		size_t n = read(efd[0], &w, 1);
		close(efd[0]);
		if (n != 0) {
			// Child died, reap it.
			waitpid(pid, NULL, 0);
			return -1;
		}
		return pid;
	}

	// Fork succeeded, we are in the child.
	int pret, cret;
	char sig;

	close(fd[1]);
	read(fd[0], &sig, 1);
	close(fd[0]);

	// Create a new process group.
	if (setpgid(0, 0) < 0) {
		perror("setpgid");
		exit(1);
	}

	// Set errno to zero before a call to ptrace.
	// It is documented that ptrace can return -1 even
	// for successful calls.
	errno = 0;
	pret = ptrace(PT_TRACE_ME, 0, 0, 0);
	if (pret != 0 && errno != 0) {
		perror("ptrace");
		exit(1);
	}

	// Change working directory if wd is not empty.
	if (wd && wd[0]) {
		errno = 0;
		cret = chdir(wd);
		if (cret != 0 && errno != 0) {
			char *error_msg;
			asprintf(&error_msg, "%s '%s'", "chdir", wd);
			perror(error_msg);
			exit(1);
		}
	}

	errno = 0;
	pret = ptrace(PT_SIGEXC, 0, 0, 0);
	if (pret != 0 && errno != 0) {
		perror("ptrace");
		exit(1);
	}

	sleep(1);

	// Create the child process.
	execve(argv0, argv, environ);

	// We should never reach here, but if we did something went wrong.
	// Write a message to parent to alert that exec failed.
	char msg = 'd';
	write(efd[1], &msg, 1);
	close(efd[1]);

	exit(1);
}