Subject: clone3() example code

Hello Christian,

Do you have some example user-space code somewhere that illustrates
calling clone3(). I'm looking at how we might document that system
call in the manual page, and some example code to play with would be
useful at this point. (I already have a simple working test program,
but probably you have something better.)

Thanks,

Michael

--
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/


2019-10-25 19:32:45

by Christian Brauner

[permalink] [raw]
Subject: Re: clone3() example code


On Fri, Oct 25, 2019 at 10:13:23AM +0200, Michael Kerrisk (man-pages) wrote:
> Hello Christian,

Hey Michael,

>
> Do you have some example user-space code somewhere that illustrates
> calling clone3(). I'm looking at how we might document that system
> call in the manual page, and some example code to play with would be

Excellent!

> useful at this point. (I already have a simple working test program,
> but probably you have something better.)

Not sure about something better but one simple thing I used (a more
extensive test-suite is coming):

#define _GNU_SOURCE
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>

#ifndef CLONE_PIDFD
#define CLONE_PIDFD 0x00001000
#endif

#ifndef __NR_clone3
#define __NR_clone3 -1
#endif

static pid_t sys_clone3(struct clone_args *args)
{
return syscall(__NR_clone3, args, sizeof(struct clone_args));
}

static int wait_for_pid(pid_t pid)
{
int status, ret;

again:
ret = waitpid(pid, &status, 0);
if (ret == -1) {
if (errno == EINTR)
goto again;

return -1;
}

if (ret != pid)
goto again;

if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
return -1;

return 0;
}

#define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr)))

int main(int argc, char *argv[])
{
int pidfd = -1;
pid_t parent_tid = -1, pid = -1;
struct clone_args args = {0};

args.parent_tid = ptr_to_u64(&parent_tid); /* CLONE_PARENT_SETTID */
args.pidfd = ptr_to_u64(&pidfd); /* CLONE_PIDFD */
args.flags = CLONE_PIDFD | CLONE_PARENT_SETTID;
args.exit_signal = SIGCHLD;

pid = sys_clone3(&args);
if (pid < 0) {
fprintf(stderr, "%s - Failed to create new process\n", strerror(errno));
exit(EXIT_FAILURE);
}

if (pid == 0) {
printf("Child process with pid %d\n", getpid());
exit(EXIT_SUCCESS);
}

printf("Parent process received child's pid %d as return value\n", pid);
printf("Parent process received child's pidfd %d\n", *(int *)args.pidfd);
printf("Parent process received child's pid %d as return argument\n",
*(pid_t *)args.parent_tid);

if (0) {
if (waitid(P_ALL, pid, NULL, 0) == 0) {
fprintf(stderr, "Managed to wait on CLONE_NO_WAITALL process with waitid(P_ALL)\n");
exit(EXIT_FAILURE);
}
printf("Child process %d requested CLONE_NO_WAITALL\n", pid);
} else {
printf("Child process %d did not request CLONE_NO_WAITALL\n", pid);
}

if (wait_for_pid(pid))
exit(EXIT_FAILURE);

if (pid != *(pid_t *)args.parent_tid)
exit(EXIT_FAILURE);

close(pidfd);

return 0;
}

Thanks!
Christian

2019-10-25 19:38:30

by Aleksa Sarai

[permalink] [raw]
Subject: Re: clone3() example code

On 2019-10-25, Christian Brauner <[email protected]> wrote:
> #define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr)))
>
> int main(int argc, char *argv[])
> {
> int pidfd = -1;
> pid_t parent_tid = -1, pid = -1;
> struct clone_args args = {0};
>
> args.parent_tid = ptr_to_u64(&parent_tid); /* CLONE_PARENT_SETTID */
> args.pidfd = ptr_to_u64(&pidfd); /* CLONE_PIDFD */
> args.flags = CLONE_PIDFD | CLONE_PARENT_SETTID;
> args.exit_signal = SIGCHLD;
>
> pid = sys_clone3(&args);

I'd suggest that

struct clone_args args = {
.flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
.parent_tid = ptr_to_u64(&parent_tid), /* CLONE_PARENT_SETTID */
.pidfd = ptr_to_u64(&pidfd), /* CLONE_PIDFD */
.exit_signal = SIGCHLD,
};

or alternatively

pid = sys_clone3(&(struct clone_args) {
.flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
.parent_tid = ptr_to_u64(&parent_tid), /* CLONE_PARENT_SETTID */
.pidfd = ptr_to_u64(&pidfd), /* CLONE_PIDFD */
.exit_signal = SIGCHLD,
});

are easier to read.

> if (pid < 0) {
> fprintf(stderr, "%s - Failed to create new process\n", strerror(errno));
> exit(EXIT_FAILURE);
> }
>
> if (pid == 0) {
> printf("Child process with pid %d\n", getpid());
> exit(EXIT_SUCCESS);
> }
>
> printf("Parent process received child's pid %d as return value\n", pid);
> printf("Parent process received child's pidfd %d\n", *(int *)args.pidfd);
> printf("Parent process received child's pid %d as return argument\n",
> *(pid_t *)args.parent_tid);
>
> if (0) {
> if (waitid(P_ALL, pid, NULL, 0) == 0) {
> fprintf(stderr, "Managed to wait on CLONE_NO_WAITALL process with waitid(P_ALL)\n");
> exit(EXIT_FAILURE);
> }
> printf("Child process %d requested CLONE_NO_WAITALL\n", pid);
> } else {
> printf("Child process %d did not request CLONE_NO_WAITALL\n", pid);
> }
>
> if (wait_for_pid(pid))
> exit(EXIT_FAILURE);
>
> if (pid != *(pid_t *)args.parent_tid)
> exit(EXIT_FAILURE);
>
> close(pidfd);
>
> return 0;
> }

--
Aleksa Sarai
Senior Software Engineer (Containers)
SUSE Linux GmbH
<https://www.cyphar.com/>


Attachments:
(No filename) (2.15 kB)
signature.asc (235.00 B)
Download all attachments

2019-10-25 19:39:05

by Christian Brauner

[permalink] [raw]
Subject: Re: clone3() example code

On Fri, Oct 25, 2019 at 08:49:56PM +1100, Aleksa Sarai wrote:
> On 2019-10-25, Christian Brauner <[email protected]> wrote:
> > #define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr)))
> >
> > int main(int argc, char *argv[])
> > {
> > int pidfd = -1;
> > pid_t parent_tid = -1, pid = -1;
> > struct clone_args args = {0};
> >
> > args.parent_tid = ptr_to_u64(&parent_tid); /* CLONE_PARENT_SETTID */
> > args.pidfd = ptr_to_u64(&pidfd); /* CLONE_PIDFD */
> > args.flags = CLONE_PIDFD | CLONE_PARENT_SETTID;
> > args.exit_signal = SIGCHLD;
> >
> > pid = sys_clone3(&args);
>
> I'd suggest that
>
> struct clone_args args = {
> .flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
> .parent_tid = ptr_to_u64(&parent_tid), /* CLONE_PARENT_SETTID */
> .pidfd = ptr_to_u64(&pidfd), /* CLONE_PIDFD */
> .exit_signal = SIGCHLD,
> };
>
> or alternatively
>
> pid = sys_clone3(&(struct clone_args) {
> .flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
> .parent_tid = ptr_to_u64(&parent_tid), /* CLONE_PARENT_SETTID */
> .pidfd = ptr_to_u64(&pidfd), /* CLONE_PIDFD */
> .exit_signal = SIGCHLD,
> });
>
> are easier to read.

That was an accident. I posted from the wrong file. The correct code is:

#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#include <errno.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#ifndef CLONE_PIDFD
#define CLONE_PIDFD 0x00001000
#endif

#ifndef __NR_clone3
#define __NR_clone3 -1
struct clone_args {
__aligned_u64 flags;
__aligned_u64 pidfd;
__aligned_u64 child_tid;
__aligned_u64 parent_tid;
__aligned_u64 exit_signal;
__aligned_u64 stack;
__aligned_u64 stack_size;
__aligned_u64 tls;
};
#endif

static pid_t sys_clone3(struct clone_args *args)
{
return syscall(__NR_clone3, args, sizeof(struct clone_args));
}

static int wait_for_pid(pid_t pid)
{
int status, ret;

again:
ret = waitpid(pid, &status, 0);
if (ret == -1) {
if (errno == EINTR)
goto again;

return -1;
}

if (ret != pid)
goto again;

if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
return -1;

return 0;
}

#define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr)))

int main(int argc, char *argv[])
{
int pidfd = -1;
pid_t parent_tid = -1, pid = -1;
struct clone_args args = {
/* CLONE_PARENT_SETTID */
.parent_tid = ptr_to_u64(&parent_tid),
/* CLONE_PIDFD */
.pidfd = ptr_to_u64(&pidfd),
.flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
.exit_signal = SIGCHLD,
};

pid = sys_clone3(&args);
if (pid < 0) {
fprintf(stderr, "%s - Failed to create new process\n", strerror(errno));
exit(EXIT_FAILURE);
}

if (pid == 0) {
printf("Child process with pid %d\n", getpid());
exit(EXIT_SUCCESS);
}

printf("Parent process received child's pid %d as return value\n", pid);
printf("Parent process received child's pidfd %d\n", pidfd);
printf("Parent process received child's pid %d as return argument\n",
*(pid_t *)args.parent_tid);

if (wait_for_pid(pid)) {
fprintf(stderr, "Failed to wait on child process\n");
exit(EXIT_FAILURE);
}

if (pid != parent_tid)
exit(EXIT_FAILURE);

close(pidfd);

return 0;
}