mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-08 22:28:24 +02:00
Remove a few more signal unsafe function calls between fork() and exec()
This commit is contained in:
@@ -23,54 +23,73 @@ serialize_string_tuple(PyObject *src) {
|
||||
|
||||
extern char **environ;
|
||||
|
||||
static inline void
|
||||
write_to_stderr(const char *text) {
|
||||
size_t sz = strlen(text);
|
||||
size_t written = 0;
|
||||
while(written < sz) {
|
||||
ssize_t amt = write(2, text + written, sz - written);
|
||||
if (amt == 0) break;
|
||||
if (amt < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR) continue;
|
||||
break;
|
||||
}
|
||||
written += amt;
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
spawn(PyObject *self UNUSED, PyObject *args) {
|
||||
PyObject *argv_p, *env_p;
|
||||
int master, slave, stdin_read_fd, stdin_write_fd;
|
||||
char* cwd;
|
||||
if (!PyArg_ParseTuple(args, "sO!O!iiii", &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd)) return NULL;
|
||||
char name[2048] = {0};
|
||||
if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||
char **argv = serialize_string_tuple(argv_p);
|
||||
char **env = serialize_string_tuple(env_p);
|
||||
|
||||
#define exit_on_err(m) { write_to_stderr(m); write_to_stderr(": "); write_to_stderr(strerror(errno)); exit(EXIT_FAILURE); }
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
// child
|
||||
// We cannot use malloc before exec() as it might deadlock if a thread in the parent process is in the middle of a malloc itself
|
||||
// Use only signal-safe functions (man 7 signal-safety)
|
||||
if (chdir(cwd) != 0) { if (chdir("/") != 0) {} }; // ignore failure to chdir to /
|
||||
if (setsid() == -1) { perror("setsid() in child process failed"); exit(EXIT_FAILURE); }
|
||||
if (dup2(slave, 1) == -1) { perror("dup2() failed for fd number 1"); exit(EXIT_FAILURE); }
|
||||
if (dup2(slave, 2) == -1) { perror("dup2() failed for fd number 2"); exit(EXIT_FAILURE); }
|
||||
if (setsid() == -1) exit_on_err("setsid() in child process failed");
|
||||
if (dup2(slave, 1) == -1) exit_on_err("dup2() failed for fd number 1");
|
||||
if (dup2(slave, 2) == -1) exit_on_err("dup2() failed for fd number 2");
|
||||
if (stdin_read_fd > -1) {
|
||||
if (dup2(stdin_read_fd, 0) == -1) { perror("dup2() failed for fd number 0"); exit(EXIT_FAILURE); }
|
||||
if (dup2(stdin_read_fd, 0) == -1) exit_on_err("dup2() failed for fd number 0");
|
||||
close(stdin_read_fd);
|
||||
close(stdin_write_fd);
|
||||
} else {
|
||||
if (dup2(slave, 0) == -1) { perror("dup2() failed for fd number 0"); exit(EXIT_FAILURE); }
|
||||
if (dup2(slave, 0) == -1) exit_on_err("dup2() failed for fd number 0");
|
||||
}
|
||||
close(slave);
|
||||
close(master);
|
||||
for (int c = 3; c < 201; c++) close(c);
|
||||
|
||||
// Establish the controlling terminal (see man 7 credentials)
|
||||
char *name = ttyname(1);
|
||||
if (name == NULL) { perror("Failed to call ttyname()"); exit(EXIT_FAILURE); }
|
||||
int tfd = open(name, O_RDWR);
|
||||
if (tfd == -1) { perror("Failed to open controlling terminal"); exit(EXIT_FAILURE); }
|
||||
if (tfd == -1) exit_on_err("Failed to open controlling terminal");
|
||||
close(tfd);
|
||||
|
||||
environ = env;
|
||||
execvp(argv[0], argv);
|
||||
// Report the failure and exec a shell instead, so that we are not left
|
||||
// with a forked but not execed process
|
||||
fprintf(stderr, "Failed to launch child: %s\nWith error: %s [%d]\n", argv[0], strerror(errno), errno);
|
||||
fprintf(stderr, "Press Enter to exit.\n");
|
||||
fflush(stderr);
|
||||
// with a forked but not exec'ed process
|
||||
write_to_stderr("Failed to launch child: ");
|
||||
write_to_stderr(argv[0]);
|
||||
write_to_stderr("\nWith error: ");
|
||||
write_to_stderr(strerror(errno));
|
||||
write_to_stderr("\nPress Enter to exit.\n");
|
||||
execlp("sh", "sh", "-c", "read w", NULL);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
free(argv);
|
||||
free(env);
|
||||
}
|
||||
#undef exit_on_err
|
||||
return PyLong_FromLong(pid);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user