Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3d96450

Browse files
committed
eio_linux: fallback to fork if clone3 is unavailable
`clone3` is blocked by Docker's default security policy. Also, use `(uintptr_t)` when storing a pointer in a `uint64_t` field, otherwise it doesn't work on 32-bit systems.
1 parent 1cd01b5 commit 3d96450

File tree

1 file changed

+47
-5
lines changed

1 file changed

+47
-5
lines changed

lib_eio_linux/eio_stubs.c

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <sys/random.h>
99
#endif
1010
#include <sys/syscall.h>
11+
#include <sys/wait.h>
1112
#include <limits.h>
1213
#include <errno.h>
1314
#include <dirent.h>
@@ -30,6 +31,9 @@
3031
#ifndef SYS_pidfd_send_signal
3132
# define SYS_pidfd_send_signal 424
3233
#endif
34+
#ifndef SYS_pidfd_open
35+
# define SYS_pidfd_open 434
36+
#endif
3337
#ifndef SYS_clone3
3438
# define SYS_clone3 435
3539
# define CLONE_PIDFD 0x00001000
@@ -148,24 +152,62 @@ CAMLprim value caml_eio_pidfd_send_signal(value v_pidfd, value v_signal) {
148152
CAMLreturn(Val_unit);
149153
}
150154

155+
static int pidfd_open(pid_t pid, unsigned int flags) {
156+
return syscall(SYS_pidfd_open, pid, flags);
157+
}
158+
159+
/* Like clone3, but falls back to fork if not supported.
160+
Also, raises exceptions rather then returning an error. */
161+
static pid_t clone3_with_fallback(struct clone_args *cl_args) {
162+
int *pidfd = (int *)(uintptr_t) cl_args->pidfd;
163+
pid_t child_pid = syscall(SYS_clone3, cl_args, sizeof(struct clone_args));
164+
165+
if (child_pid >= 0)
166+
return child_pid; /* Success! */
167+
168+
if (errno != ENOSYS && errno != EPERM) {
169+
uerror("clone3", Nothing); /* Unknown error */
170+
}
171+
172+
/* Probably Docker's security policy is blocking clone3. Fall back to forking. */
173+
174+
child_pid = fork();
175+
if (child_pid == 0) {
176+
/* We are the child */
177+
return 0;
178+
} else if (child_pid < 0) {
179+
uerror("fork", Nothing);
180+
}
181+
182+
*pidfd = pidfd_open(child_pid, 0); /* Is automatically close-on-exec */
183+
if (*pidfd < 0) {
184+
int e = errno;
185+
kill(child_pid, SIGKILL);
186+
waitpid(child_pid, NULL, 0);
187+
errno = e;
188+
uerror("pidfd_open", Nothing);
189+
}
190+
191+
return child_pid;
192+
}
193+
151194
CAMLprim value caml_eio_clone3(value v_errors, value v_actions) {
152195
CAMLparam1(v_actions);
153196
CAMLlocal1(v_result);
154197
pid_t child_pid;
155198
int pidfd = -1; /* Is automatically close-on-exec */
156199
struct clone_args cl_args = {
157200
.flags = CLONE_PIDFD,
158-
.pidfd = (uint64_t) &pidfd,
201+
.pidfd = (uintptr_t) &pidfd,
159202
.exit_signal = SIGCHLD, /* Needed for wait4 to work if we exit before exec */
160-
.stack = (uint64_t) NULL, /* Use copy-on-write parent stack */
203+
.stack = (uintptr_t) NULL, /* Use copy-on-write parent stack */
161204
.stack_size = 0,
162205
};
163206

164-
child_pid = syscall(SYS_clone3, &cl_args, sizeof(struct clone_args));
207+
child_pid = clone3_with_fallback(&cl_args);
165208
if (child_pid == 0) {
209+
/* Run child actions (doesn't return) */
166210
eio_unix_run_fork_actions(Int_val(v_errors), v_actions);
167-
} else if (child_pid < 0) {
168-
uerror("clone3", Nothing);
169211
}
170212

171213
v_result = caml_alloc_tuple(2);

0 commit comments

Comments
 (0)