Skip to content

Commit 17a8106

Browse files
adrianreberChristian Brauner
authored andcommitted
selftests: add tests for clone3()
This adds tests for clone3() with different values and sizes of struct clone_args. This selftest was initially part of of the clone3() with PID selftest. After that patch was almost merged Eugene sent out a couple of patches to fix problems with these test. This commit now only contains the clone3() selftest after the LPC decision to rework clone3() with PID to allow setting the PID in multiple PID namespaces including all of Eugene's patches. Signed-off-by: Eugene Syromiatnikov <[email protected]> Signed-off-by: Adrian Reber <[email protected]> Reviewed-by: Christian Brauner <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent de52872 commit 17a8106

File tree

3 files changed

+208
-2
lines changed

3 files changed

+208
-2
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
clone3
12
clone3_clear_sighand
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# SPDX-License-Identifier: GPL-2.0-only
1+
# SPDX-License-Identifier: GPL-2.0
22
CFLAGS += -g -I../../../../usr/include/
33

4-
TEST_GEN_PROGS := clone3_clear_sighand
4+
TEST_GEN_PROGS := clone3 clone3_clear_sighand
55

66
include ../lib.mk
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
/* Based on Christian Brauner's clone3() example */
4+
5+
#define _GNU_SOURCE
6+
#include <errno.h>
7+
#include <inttypes.h>
8+
#include <linux/types.h>
9+
#include <linux/sched.h>
10+
#include <stdint.h>
11+
#include <stdio.h>
12+
#include <stdlib.h>
13+
#include <sys/syscall.h>
14+
#include <sys/types.h>
15+
#include <sys/un.h>
16+
#include <sys/wait.h>
17+
#include <unistd.h>
18+
#include <sched.h>
19+
20+
#include "../kselftest.h"
21+
22+
/*
23+
* Different sizes of struct clone_args
24+
*/
25+
#ifndef CLONE3_ARGS_SIZE_V0
26+
#define CLONE3_ARGS_SIZE_V0 64
27+
#endif
28+
29+
enum test_mode {
30+
CLONE3_ARGS_NO_TEST,
31+
CLONE3_ARGS_ALL_0,
32+
CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG,
33+
CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG,
34+
CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG,
35+
CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG,
36+
};
37+
38+
static pid_t raw_clone(struct clone_args *args, size_t size)
39+
{
40+
return syscall(__NR_clone3, args, size);
41+
}
42+
43+
static int call_clone3(uint64_t flags, size_t size, enum test_mode test_mode)
44+
{
45+
struct clone_args args = {
46+
.flags = flags,
47+
.exit_signal = SIGCHLD,
48+
};
49+
50+
struct clone_args_extended {
51+
struct clone_args args;
52+
__aligned_u64 excess_space[2];
53+
} args_ext;
54+
55+
pid_t pid = -1;
56+
int status;
57+
58+
memset(&args_ext, 0, sizeof(args_ext));
59+
if (size > sizeof(struct clone_args))
60+
args_ext.excess_space[1] = 1;
61+
62+
if (size == 0)
63+
size = sizeof(struct clone_args);
64+
65+
switch (test_mode) {
66+
case CLONE3_ARGS_ALL_0:
67+
args.flags = 0;
68+
args.exit_signal = 0;
69+
break;
70+
case CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG:
71+
args.exit_signal = 0xbadc0ded00000000ULL;
72+
break;
73+
case CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG:
74+
args.exit_signal = 0x0000000080000000ULL;
75+
break;
76+
case CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG:
77+
args.exit_signal = 0x0000000000000100ULL;
78+
break;
79+
case CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG:
80+
args.exit_signal = 0x00000000000000f0ULL;
81+
break;
82+
}
83+
84+
memcpy(&args_ext.args, &args, sizeof(struct clone_args));
85+
86+
pid = raw_clone((struct clone_args *)&args_ext, size);
87+
if (pid < 0) {
88+
ksft_print_msg("%s - Failed to create new process\n",
89+
strerror(errno));
90+
return -errno;
91+
}
92+
93+
if (pid == 0) {
94+
ksft_print_msg("I am the child, my PID is %d\n", getpid());
95+
_exit(EXIT_SUCCESS);
96+
}
97+
98+
ksft_print_msg("I am the parent (%d). My child's pid is %d\n",
99+
getpid(), pid);
100+
101+
if (waitpid(-1, &status, __WALL) < 0) {
102+
ksft_print_msg("Child returned %s\n", strerror(errno));
103+
return -errno;
104+
}
105+
if (WEXITSTATUS(status))
106+
return WEXITSTATUS(status);
107+
108+
return 0;
109+
}
110+
111+
static void test_clone3(uint64_t flags, size_t size, int expected,
112+
enum test_mode test_mode)
113+
{
114+
int ret;
115+
116+
ksft_print_msg(
117+
"[%d] Trying clone3() with flags %#" PRIx64 " (size %zu)\n",
118+
getpid(), flags, size);
119+
ret = call_clone3(flags, size, test_mode);
120+
ksft_print_msg("[%d] clone3() with flags says: %d expected %d\n",
121+
getpid(), ret, expected);
122+
if (ret != expected)
123+
ksft_test_result_fail(
124+
"[%d] Result (%d) is different than expected (%d)\n",
125+
getpid(), ret, expected);
126+
else
127+
ksft_test_result_pass(
128+
"[%d] Result (%d) matches expectation (%d)\n",
129+
getpid(), ret, expected);
130+
}
131+
132+
int main(int argc, char *argv[])
133+
{
134+
pid_t pid;
135+
136+
uid_t uid = getuid();
137+
138+
ksft_print_header();
139+
ksft_set_plan(17);
140+
141+
/* Just a simple clone3() should return 0.*/
142+
test_clone3(0, 0, 0, CLONE3_ARGS_NO_TEST);
143+
144+
/* Do a clone3() in a new PID NS.*/
145+
if (uid == 0)
146+
test_clone3(CLONE_NEWPID, 0, 0, CLONE3_ARGS_NO_TEST);
147+
else
148+
ksft_test_result_skip("Skipping clone3() with CLONE_NEWPID\n");
149+
150+
/* Do a clone3() with CLONE3_ARGS_SIZE_V0. */
151+
test_clone3(0, CLONE3_ARGS_SIZE_V0, 0, CLONE3_ARGS_NO_TEST);
152+
153+
/* Do a clone3() with CLONE3_ARGS_SIZE_V0 - 8 */
154+
test_clone3(0, CLONE3_ARGS_SIZE_V0 - 8, -EINVAL, CLONE3_ARGS_NO_TEST);
155+
156+
/* Do a clone3() with sizeof(struct clone_args) + 8 */
157+
test_clone3(0, sizeof(struct clone_args) + 8, 0, CLONE3_ARGS_NO_TEST);
158+
159+
/* Do a clone3() with exit_signal having highest 32 bits non-zero */
160+
test_clone3(0, 0, -EINVAL, CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG);
161+
162+
/* Do a clone3() with negative 32-bit exit_signal */
163+
test_clone3(0, 0, -EINVAL, CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG);
164+
165+
/* Do a clone3() with exit_signal not fitting into CSIGNAL mask */
166+
test_clone3(0, 0, -EINVAL, CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG);
167+
168+
/* Do a clone3() with NSIG < exit_signal < CSIG */
169+
test_clone3(0, 0, -EINVAL, CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG);
170+
171+
test_clone3(0, sizeof(struct clone_args) + 8, 0, CLONE3_ARGS_ALL_0);
172+
173+
test_clone3(0, sizeof(struct clone_args) + 16, -E2BIG,
174+
CLONE3_ARGS_ALL_0);
175+
176+
test_clone3(0, sizeof(struct clone_args) * 2, -E2BIG,
177+
CLONE3_ARGS_ALL_0);
178+
179+
/* Do a clone3() with > page size */
180+
test_clone3(0, getpagesize() + 8, -E2BIG, CLONE3_ARGS_NO_TEST);
181+
182+
/* Do a clone3() with CLONE3_ARGS_SIZE_V0 in a new PID NS. */
183+
if (uid == 0)
184+
test_clone3(CLONE_NEWPID, CLONE3_ARGS_SIZE_V0, 0,
185+
CLONE3_ARGS_NO_TEST);
186+
else
187+
ksft_test_result_skip("Skipping clone3() with CLONE_NEWPID\n");
188+
189+
/* Do a clone3() with CLONE3_ARGS_SIZE_V0 - 8 in a new PID NS */
190+
test_clone3(CLONE_NEWPID, CLONE3_ARGS_SIZE_V0 - 8, -EINVAL,
191+
CLONE3_ARGS_NO_TEST);
192+
193+
/* Do a clone3() with sizeof(struct clone_args) + 8 in a new PID NS */
194+
if (uid == 0)
195+
test_clone3(CLONE_NEWPID, sizeof(struct clone_args) + 8, 0,
196+
CLONE3_ARGS_NO_TEST);
197+
else
198+
ksft_test_result_skip("Skipping clone3() with CLONE_NEWPID\n");
199+
200+
/* Do a clone3() with > page size in a new PID NS */
201+
test_clone3(CLONE_NEWPID, getpagesize() + 8, -E2BIG,
202+
CLONE3_ARGS_NO_TEST);
203+
204+
return !ksft_get_fail_cnt() ? ksft_exit_pass() : ksft_exit_fail();
205+
}

0 commit comments

Comments
 (0)