Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: add tests for clone3() with *set_tid

This tests clone3() with *set_tid to see if all desired PIDs are working
as expected. The tests are trying multiple invalid input parameters as
well as creating processes while specifying a certain PID in multiple
PID namespaces at the same time.

Additionally this moves common clone3() test code into clone3_selftests.h.

Signed-off-by: Adrian Reber <areber@redhat.com>
Acked-by: Christian Brauner <christian.brauner@ubuntu.com>
Link: https://lore.kernel.org/r/20191115123621.142252-2-areber@redhat.com
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>

authored by

Adrian Reber and committed by
Christian Brauner
41585bbe 49cb2fc4

+421 -26
+1
tools/testing/selftests/clone3/.gitignore
··· 1 1 clone3 2 2 clone3_clear_sighand 3 + clone3_set_tid
+1 -1
tools/testing/selftests/clone3/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 CFLAGS += -g -I../../../../usr/include/ 3 3 4 - TEST_GEN_PROGS := clone3 clone3_clear_sighand 4 + TEST_GEN_PROGS := clone3 clone3_clear_sighand clone3_set_tid 5 5 6 6 include ../lib.mk
+2 -6
tools/testing/selftests/clone3/clone3.c
··· 18 18 #include <sched.h> 19 19 20 20 #include "../kselftest.h" 21 + #include "clone3_selftests.h" 21 22 22 23 /* 23 24 * Different sizes of struct clone_args ··· 35 34 CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG, 36 35 CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG, 37 36 }; 38 - 39 - static pid_t raw_clone(struct clone_args *args, size_t size) 40 - { 41 - return syscall(__NR_clone3, args, size); 42 - } 43 37 44 38 static int call_clone3(uint64_t flags, size_t size, enum test_mode test_mode) 45 39 { ··· 79 83 80 84 memcpy(&args_ext.args, &args, sizeof(struct clone_args)); 81 85 82 - pid = raw_clone((struct clone_args *)&args_ext, size); 86 + pid = sys_clone3((struct clone_args *)&args_ext, size); 83 87 if (pid < 0) { 84 88 ksft_print_msg("%s - Failed to create new process\n", 85 89 strerror(errno));
+1 -19
tools/testing/selftests/clone3/clone3_clear_sighand.c
··· 14 14 #include <sys/wait.h> 15 15 16 16 #include "../kselftest.h" 17 + #include "clone3_selftests.h" 17 18 18 19 #ifndef CLONE_CLEAR_SIGHAND 19 20 #define CLONE_CLEAR_SIGHAND 0x100000000ULL 20 21 #endif 21 - 22 - #ifndef __NR_clone3 23 - #define __NR_clone3 -1 24 - struct clone_args { 25 - __aligned_u64 flags; 26 - __aligned_u64 pidfd; 27 - __aligned_u64 child_tid; 28 - __aligned_u64 parent_tid; 29 - __aligned_u64 exit_signal; 30 - __aligned_u64 stack; 31 - __aligned_u64 stack_size; 32 - __aligned_u64 tls; 33 - }; 34 - #endif 35 - 36 - static pid_t sys_clone3(struct clone_args *args, size_t size) 37 - { 38 - return syscall(__NR_clone3, args, size); 39 - } 40 22 41 23 static void test_clone3_supported(void) 42 24 {
+35
tools/testing/selftests/clone3/clone3_selftests.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + #ifndef _CLONE3_SELFTESTS_H 4 + #define _CLONE3_SELFTESTS_H 5 + 6 + #define _GNU_SOURCE 7 + #include <sched.h> 8 + #include <stdint.h> 9 + #include <syscall.h> 10 + #include <linux/types.h> 11 + 12 + #define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr))) 13 + 14 + #ifndef __NR_clone3 15 + #define __NR_clone3 -1 16 + struct clone_args { 17 + __aligned_u64 flags; 18 + __aligned_u64 pidfd; 19 + __aligned_u64 child_tid; 20 + __aligned_u64 parent_tid; 21 + __aligned_u64 exit_signal; 22 + __aligned_u64 stack; 23 + __aligned_u64 stack_size; 24 + __aligned_u64 tls; 25 + __aligned_u64 set_tid; 26 + __aligned_u64 set_tid_size; 27 + }; 28 + #endif 29 + 30 + static pid_t sys_clone3(struct clone_args *args, size_t size) 31 + { 32 + return syscall(__NR_clone3, args, size); 33 + } 34 + 35 + #endif /* _CLONE3_SELFTESTS_H */
+381
tools/testing/selftests/clone3/clone3_set_tid.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + /* 4 + * Based on Christian Brauner's clone3() example. 5 + * These tests are assuming to be running in the host's 6 + * PID namespace. 7 + */ 8 + 9 + #define _GNU_SOURCE 10 + #include <errno.h> 11 + #include <linux/types.h> 12 + #include <linux/sched.h> 13 + #include <stdio.h> 14 + #include <stdlib.h> 15 + #include <stdbool.h> 16 + #include <sys/syscall.h> 17 + #include <sys/types.h> 18 + #include <sys/un.h> 19 + #include <sys/wait.h> 20 + #include <unistd.h> 21 + #include <sched.h> 22 + 23 + #include "../kselftest.h" 24 + #include "clone3_selftests.h" 25 + 26 + #ifndef MAX_PID_NS_LEVEL 27 + #define MAX_PID_NS_LEVEL 32 28 + #endif 29 + 30 + static int pipe_1[2]; 31 + static int pipe_2[2]; 32 + 33 + static int call_clone3_set_tid(pid_t *set_tid, 34 + size_t set_tid_size, 35 + int flags, 36 + int expected_pid, 37 + bool wait_for_it) 38 + { 39 + int status; 40 + pid_t pid = -1; 41 + 42 + struct clone_args args = { 43 + .flags = flags, 44 + .exit_signal = SIGCHLD, 45 + .set_tid = ptr_to_u64(set_tid), 46 + .set_tid_size = set_tid_size, 47 + }; 48 + 49 + pid = sys_clone3(&args, sizeof(struct clone_args)); 50 + if (pid < 0) { 51 + ksft_print_msg("%s - Failed to create new process\n", 52 + strerror(errno)); 53 + return -errno; 54 + } 55 + 56 + if (pid == 0) { 57 + int ret; 58 + char tmp = 0; 59 + int exit_code = EXIT_SUCCESS; 60 + 61 + ksft_print_msg("I am the child, my PID is %d (expected %d)\n", 62 + getpid(), set_tid[0]); 63 + if (wait_for_it) { 64 + ksft_print_msg("[%d] Child is ready and waiting\n", 65 + getpid()); 66 + 67 + /* Signal the parent that the child is ready */ 68 + close(pipe_1[0]); 69 + ret = write(pipe_1[1], &tmp, 1); 70 + if (ret != 1) { 71 + ksft_print_msg( 72 + "Writing to pipe returned %d", ret); 73 + exit_code = EXIT_FAILURE; 74 + } 75 + close(pipe_1[1]); 76 + close(pipe_2[1]); 77 + ret = read(pipe_2[0], &tmp, 1); 78 + if (ret != 1) { 79 + ksft_print_msg( 80 + "Reading from pipe returned %d", ret); 81 + exit_code = EXIT_FAILURE; 82 + } 83 + close(pipe_2[0]); 84 + } 85 + 86 + if (set_tid[0] != getpid()) 87 + _exit(EXIT_FAILURE); 88 + _exit(exit_code); 89 + } 90 + 91 + if (expected_pid == 0 || expected_pid == pid) { 92 + ksft_print_msg("I am the parent (%d). My child's pid is %d\n", 93 + getpid(), pid); 94 + } else { 95 + ksft_print_msg( 96 + "Expected child pid %d does not match actual pid %d\n", 97 + expected_pid, pid); 98 + return -1; 99 + } 100 + 101 + if (waitpid(pid, &status, 0) < 0) { 102 + ksft_print_msg("Child returned %s\n", strerror(errno)); 103 + return -errno; 104 + } 105 + 106 + if (!WIFEXITED(status)) 107 + return -1; 108 + 109 + return WEXITSTATUS(status); 110 + } 111 + 112 + static void test_clone3_set_tid(pid_t *set_tid, 113 + size_t set_tid_size, 114 + int flags, 115 + int expected, 116 + int expected_pid, 117 + bool wait_for_it) 118 + { 119 + int ret; 120 + 121 + ksft_print_msg( 122 + "[%d] Trying clone3() with CLONE_SET_TID to %d and 0x%x\n", 123 + getpid(), set_tid[0], flags); 124 + ret = call_clone3_set_tid(set_tid, set_tid_size, flags, expected_pid, 125 + wait_for_it); 126 + ksft_print_msg( 127 + "[%d] clone3() with CLONE_SET_TID %d says :%d - expected %d\n", 128 + getpid(), set_tid[0], ret, expected); 129 + if (ret != expected) 130 + ksft_test_result_fail( 131 + "[%d] Result (%d) is different than expected (%d)\n", 132 + getpid(), ret, expected); 133 + else 134 + ksft_test_result_pass( 135 + "[%d] Result (%d) matches expectation (%d)\n", 136 + getpid(), ret, expected); 137 + } 138 + int main(int argc, char *argv[]) 139 + { 140 + FILE *f; 141 + char buf; 142 + char *line; 143 + int status; 144 + int ret = -1; 145 + size_t len = 0; 146 + int pid_max = 0; 147 + uid_t uid = getuid(); 148 + char proc_path[100] = {0}; 149 + pid_t pid, ns1, ns2, ns3, ns_pid; 150 + pid_t set_tid[MAX_PID_NS_LEVEL * 2]; 151 + 152 + if (pipe(pipe_1) < 0 || pipe(pipe_2) < 0) 153 + ksft_exit_fail_msg("pipe() failed\n"); 154 + 155 + ksft_print_header(); 156 + ksft_set_plan(27); 157 + 158 + f = fopen("/proc/sys/kernel/pid_max", "r"); 159 + if (f == NULL) 160 + ksft_exit_fail_msg( 161 + "%s - Could not open /proc/sys/kernel/pid_max\n", 162 + strerror(errno)); 163 + fscanf(f, "%d", &pid_max); 164 + fclose(f); 165 + ksft_print_msg("/proc/sys/kernel/pid_max %d\n", pid_max); 166 + 167 + /* Try invalid settings */ 168 + memset(&set_tid, 0, sizeof(set_tid)); 169 + test_clone3_set_tid(set_tid, MAX_PID_NS_LEVEL + 1, 0, -EINVAL, 0, 0); 170 + 171 + test_clone3_set_tid(set_tid, MAX_PID_NS_LEVEL * 2, 0, -EINVAL, 0, 0); 172 + 173 + test_clone3_set_tid(set_tid, MAX_PID_NS_LEVEL * 2 + 1, 0, 174 + -EINVAL, 0, 0); 175 + 176 + test_clone3_set_tid(set_tid, MAX_PID_NS_LEVEL * 42, 0, -EINVAL, 0, 0); 177 + 178 + /* 179 + * This can actually work if this test running in a MAX_PID_NS_LEVEL - 1 180 + * nested PID namespace. 181 + */ 182 + test_clone3_set_tid(set_tid, MAX_PID_NS_LEVEL - 1, 0, -EINVAL, 0, 0); 183 + 184 + memset(&set_tid, 0xff, sizeof(set_tid)); 185 + test_clone3_set_tid(set_tid, MAX_PID_NS_LEVEL + 1, 0, -EINVAL, 0, 0); 186 + 187 + test_clone3_set_tid(set_tid, MAX_PID_NS_LEVEL * 2, 0, -EINVAL, 0, 0); 188 + 189 + test_clone3_set_tid(set_tid, MAX_PID_NS_LEVEL * 2 + 1, 0, 190 + -EINVAL, 0, 0); 191 + 192 + test_clone3_set_tid(set_tid, MAX_PID_NS_LEVEL * 42, 0, -EINVAL, 0, 0); 193 + 194 + /* 195 + * This can actually work if this test running in a MAX_PID_NS_LEVEL - 1 196 + * nested PID namespace. 197 + */ 198 + test_clone3_set_tid(set_tid, MAX_PID_NS_LEVEL - 1, 0, -EINVAL, 0, 0); 199 + 200 + memset(&set_tid, 0, sizeof(set_tid)); 201 + /* Try with an invalid PID */ 202 + set_tid[0] = 0; 203 + test_clone3_set_tid(set_tid, 1, 0, -EINVAL, 0, 0); 204 + 205 + set_tid[0] = -1; 206 + test_clone3_set_tid(set_tid, 1, 0, -EINVAL, 0, 0); 207 + 208 + /* Claim that the set_tid array actually contains 2 elements. */ 209 + test_clone3_set_tid(set_tid, 2, 0, -EINVAL, 0, 0); 210 + 211 + /* Try it in a new PID namespace */ 212 + if (uid == 0) 213 + test_clone3_set_tid(set_tid, 1, CLONE_NEWPID, -EINVAL, 0, 0); 214 + else 215 + ksft_test_result_skip("Clone3() with set_tid requires root\n"); 216 + 217 + /* Try with a valid PID (1) this should return -EEXIST. */ 218 + set_tid[0] = 1; 219 + if (uid == 0) 220 + test_clone3_set_tid(set_tid, 1, 0, -EEXIST, 0, 0); 221 + else 222 + ksft_test_result_skip("Clone3() with set_tid requires root\n"); 223 + 224 + /* Try it in a new PID namespace */ 225 + if (uid == 0) 226 + test_clone3_set_tid(set_tid, 1, CLONE_NEWPID, 0, 0, 0); 227 + else 228 + ksft_test_result_skip("Clone3() with set_tid requires root\n"); 229 + 230 + /* pid_max should fail everywhere */ 231 + set_tid[0] = pid_max; 232 + test_clone3_set_tid(set_tid, 1, 0, -EINVAL, 0, 0); 233 + 234 + if (uid == 0) 235 + test_clone3_set_tid(set_tid, 1, CLONE_NEWPID, -EINVAL, 0, 0); 236 + else 237 + ksft_test_result_skip("Clone3() with set_tid requires root\n"); 238 + 239 + if (uid != 0) { 240 + /* 241 + * All remaining tests require root. Tell the framework 242 + * that all those tests are skipped as non-root. 243 + */ 244 + ksft_cnt.ksft_xskip += ksft_plan - ksft_test_num(); 245 + goto out; 246 + } 247 + 248 + /* Find the current active PID */ 249 + pid = fork(); 250 + if (pid == 0) { 251 + ksft_print_msg("Child has PID %d\n", getpid()); 252 + _exit(EXIT_SUCCESS); 253 + } 254 + if (waitpid(pid, &status, 0) < 0) 255 + ksft_exit_fail_msg("Waiting for child %d failed", pid); 256 + 257 + /* After the child has finished, its PID should be free. */ 258 + set_tid[0] = pid; 259 + test_clone3_set_tid(set_tid, 1, 0, 0, 0, 0); 260 + 261 + /* This should fail as there is no PID 1 in that namespace */ 262 + test_clone3_set_tid(set_tid, 1, CLONE_NEWPID, -EINVAL, 0, 0); 263 + 264 + /* 265 + * Creating a process with PID 1 in the newly created most nested 266 + * PID namespace and PID 'pid' in the parent PID namespace. This 267 + * needs to work. 268 + */ 269 + set_tid[0] = 1; 270 + set_tid[1] = pid; 271 + test_clone3_set_tid(set_tid, 2, CLONE_NEWPID, 0, pid, 0); 272 + 273 + ksft_print_msg("unshare PID namespace\n"); 274 + if (unshare(CLONE_NEWPID) == -1) 275 + ksft_exit_fail_msg("unshare(CLONE_NEWPID) failed: %s\n", 276 + strerror(errno)); 277 + 278 + set_tid[0] = pid; 279 + 280 + /* This should fail as there is no PID 1 in that namespace */ 281 + test_clone3_set_tid(set_tid, 1, 0, -EINVAL, 0, 0); 282 + 283 + /* Let's create a PID 1 */ 284 + ns_pid = fork(); 285 + if (ns_pid == 0) { 286 + ksft_print_msg("Child in PID namespace has PID %d\n", getpid()); 287 + set_tid[0] = 2; 288 + test_clone3_set_tid(set_tid, 1, 0, 0, 2, 0); 289 + 290 + set_tid[0] = 1; 291 + set_tid[1] = -1; 292 + set_tid[2] = pid; 293 + /* This should fail as there is invalid PID at level '1'. */ 294 + test_clone3_set_tid(set_tid, 3, CLONE_NEWPID, -EINVAL, 0, 0); 295 + 296 + set_tid[0] = 1; 297 + set_tid[1] = 42; 298 + set_tid[2] = pid; 299 + /* 300 + * This should fail as there are not enough active PID 301 + * namespaces. Again assuming this is running in the host's 302 + * PID namespace. Not yet nested. 303 + */ 304 + test_clone3_set_tid(set_tid, 4, CLONE_NEWPID, -EINVAL, 0, 0); 305 + 306 + /* 307 + * This should work and from the parent we should see 308 + * something like 'NSpid: pid 42 1'. 309 + */ 310 + test_clone3_set_tid(set_tid, 3, CLONE_NEWPID, 0, 42, true); 311 + 312 + _exit(ksft_cnt.ksft_pass); 313 + } 314 + 315 + close(pipe_1[1]); 316 + close(pipe_2[0]); 317 + while (read(pipe_1[0], &buf, 1) > 0) { 318 + ksft_print_msg("[%d] Child is ready and waiting\n", getpid()); 319 + break; 320 + } 321 + 322 + snprintf(proc_path, sizeof(proc_path), "/proc/%d/status", pid); 323 + f = fopen(proc_path, "r"); 324 + if (f == NULL) 325 + ksft_exit_fail_msg( 326 + "%s - Could not open %s\n", 327 + strerror(errno), proc_path); 328 + 329 + while (getline(&line, &len, f) != -1) { 330 + if (strstr(line, "NSpid")) { 331 + int i; 332 + 333 + /* Verify that all generated PIDs are as expected. */ 334 + i = sscanf(line, "NSpid:\t%d\t%d\t%d", 335 + &ns3, &ns2, &ns1); 336 + if (i != 3) { 337 + ksft_print_msg( 338 + "Unexpected 'NSPid:' entry: %s", 339 + line); 340 + ns1 = ns2 = ns3 = 0; 341 + } 342 + break; 343 + } 344 + } 345 + fclose(f); 346 + free(line); 347 + close(pipe_2[0]); 348 + 349 + /* Tell the clone3()'d child to finish. */ 350 + write(pipe_2[1], &buf, 1); 351 + close(pipe_2[1]); 352 + 353 + if (waitpid(ns_pid, &status, 0) < 0) { 354 + ksft_print_msg("Child returned %s\n", strerror(errno)); 355 + ret = -errno; 356 + goto out; 357 + } 358 + 359 + if (!WIFEXITED(status)) 360 + ksft_test_result_fail("Child error\n"); 361 + 362 + if (WEXITSTATUS(status)) 363 + /* 364 + * Update the number of total tests with the tests from the 365 + * child processes. 366 + */ 367 + ksft_cnt.ksft_pass = WEXITSTATUS(status); 368 + 369 + if (ns3 == pid && ns2 == 42 && ns1 == 1) 370 + ksft_test_result_pass( 371 + "PIDs in all namespaces as expected (%d,%d,%d)\n", 372 + ns3, ns2, ns1); 373 + else 374 + ksft_test_result_fail( 375 + "PIDs in all namespaces not as expected (%d,%d,%d)\n", 376 + ns3, ns2, ns1); 377 + out: 378 + ret = 0; 379 + 380 + return !ret ? ksft_exit_pass() : ksft_exit_fail(); 381 + }