/* exec_edge.c -- exec replacement preserves argv and only non-CLOEXEC fds. */ #include #include #include #include #include #include static void cleanup(const char *keep_path, const char *cloexec_path) { if (keep_path) unlink(keep_path); if (cloexec_path) unlink(cloexec_path); } static int proc_file_contains(const char *path, const char *needle, size_t needle_len) { int fd = open(path, O_RDONLY); if (fd < 0) return 0; char buffer[4096]; size_t carried = 0; while (needle_len > 0 && needle_len <= sizeof(buffer)) { ssize_t length = read(fd, buffer + carried, sizeof(buffer) - carried); if (length < 0) break; size_t available = carried + (size_t)length; for (size_t offset = 0; offset + needle_len <= available; offset++) { if (memcmp(buffer + offset, needle, needle_len) == 0) { close(fd); return 1; } } if (length == 0) break; carried = available < needle_len - 1 ? available : needle_len - 1; memmove(buffer, buffer + available - carried, carried); } close(fd); return 0; } int main(int argc, char **argv) { if (argc == 7 && strcmp(argv[1], "--after") == 0) { int keep_fd = atoi(argv[2]); int cloexec_fd = atoi(argv[3]); const char *keep_path = argv[4]; const char *cloexec_path = argv[5]; errno = 0; int keep_open = fcntl(keep_fd, F_GETFD) >= 0; errno = 0; int cloexec_open_result = fcntl(cloexec_fd, F_GETFD); int cloexec_closed = cloexec_open_result == -1 && errno == EBADF; int fd_io_ok = 0; if (keep_open && lseek(keep_fd, 0, SEEK_SET) >= 0 && write(keep_fd, "ok", 2) == 2 && lseek(keep_fd, 0, SEEK_SET) >= 0) { char value[2]; fd_io_ok = read(keep_fd, value, sizeof(value)) == 2 && memcmp(value, "ok", sizeof(value)) == 0; } int argv0_ok = strcmp(argv[0], "custom-exec-argv0") == 0; int empty_arg_ok = argv[6][0] == '\0'; const char proc_argv0[] = "custom-exec-argv0\0"; const char proc_env[] = "EXEC_EDGE_ENV=committed\0"; int proc_cmdline_ok = proc_file_contains( "/proc/self/cmdline", proc_argv0, sizeof(proc_argv0) - 1); int proc_environ_ok = proc_file_contains( "/proc/self/environ", proc_env, sizeof(proc_env) - 1); int ok = argv0_ok && empty_arg_ok && keep_open && cloexec_closed && fd_io_ok && proc_cmdline_ok && proc_environ_ok; printf("exec_argv0: %s\n", argv0_ok ? "yes" : "no"); printf("exec_empty_arg: %s\n", empty_arg_ok ? "yes" : "no"); printf("exec_keep_fd: %s\n", keep_open ? "yes" : "no"); printf("exec_cloexec_closed: %s\n", cloexec_closed ? "yes" : "no"); printf("exec_fd_io: %s\n", fd_io_ok ? "yes" : "no"); printf("exec_proc_cmdline: %s\n", proc_cmdline_ok ? "yes" : "no"); printf("exec_proc_environ: %s\n", proc_environ_ok ? "yes" : "no"); printf("exec_replacement: %s\n", ok ? "ok" : "FAIL"); close(keep_fd); if (!cloexec_closed) close(cloexec_fd); cleanup(keep_path, cloexec_path); return ok ? 0 : 1; } char keep_path[64]; char cloexec_path[64]; snprintf(keep_path, sizeof(keep_path), "exec-edge-keep-%ld.tmp", (long)getpid()); snprintf(cloexec_path, sizeof(cloexec_path), "exec-edge-cloexec-%ld.tmp", (long)getpid()); int keep_fd = open(keep_path, O_CREAT | O_RDWR | O_TRUNC, 0600); int cloexec_fd = open(cloexec_path, O_CREAT | O_RDWR | O_TRUNC, 0600); if (keep_fd < 0 || cloexec_fd < 0) { perror("open"); if (keep_fd >= 0) close(keep_fd); if (cloexec_fd >= 0) close(cloexec_fd); cleanup(keep_path, cloexec_path); return 1; } if (fcntl(cloexec_fd, F_SETFD, FD_CLOEXEC) != 0) { perror("fcntl(FD_CLOEXEC)"); close(keep_fd); close(cloexec_fd); cleanup(keep_path, cloexec_path); return 1; } if (setenv("EXEC_EDGE_ENV", "committed", 1) != 0) { perror("setenv"); close(keep_fd); close(cloexec_fd); cleanup(keep_path, cloexec_path); return 1; } char keep_fd_arg[32]; char cloexec_fd_arg[32]; snprintf(keep_fd_arg, sizeof(keep_fd_arg), "%d", keep_fd); snprintf(cloexec_fd_arg, sizeof(cloexec_fd_arg), "%d", cloexec_fd); char *replacement_argv[] = { "custom-exec-argv0", "--after", keep_fd_arg, cloexec_fd_arg, keep_path, cloexec_path, "", NULL, }; execvp(argv[0], replacement_argv); perror("execvp"); close(keep_fd); close(cloexec_fd); cleanup(keep_path, cloexec_path); return 1; }