brintos

brintos / linux-shallow public Read only

0
0
Text · 10.5 KiB · bfb0da2 Raw
419 lines · c
1// SPDX-License-Identifier: GPL-2.02 3/*4 * Based on Christian Brauner's clone3() example.5 * These tests are assuming to be running in the host's6 * PID namespace.7 */8 9#define _GNU_SOURCE10#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#define MAX_PID_NS_LEVEL 3227 28static int pipe_1[2];29static int pipe_2[2];30 31static void child_exit(int ret)32{33	fflush(stdout);34	fflush(stderr);35	_exit(ret);36}37 38static int call_clone3_set_tid(pid_t *set_tid,39			       size_t set_tid_size,40			       int flags,41			       int expected_pid,42			       bool wait_for_it)43{44	int status;45	pid_t pid = -1;46 47	struct __clone_args args = {48		.flags = flags,49		.exit_signal = SIGCHLD,50		.set_tid = ptr_to_u64(set_tid),51		.set_tid_size = set_tid_size,52	};53 54	pid = sys_clone3(&args, sizeof(args));55	if (pid < 0) {56		ksft_print_msg("%s - Failed to create new process\n",57			       strerror(errno));58		return -errno;59	}60 61	if (pid == 0) {62		int ret;63		char tmp = 0;64		int exit_code = EXIT_SUCCESS;65 66		ksft_print_msg("I am the child, my PID is %d (expected %d)\n",67			       getpid(), set_tid[0]);68		if (wait_for_it) {69			ksft_print_msg("[%d] Child is ready and waiting\n",70				       getpid());71 72			/* Signal the parent that the child is ready */73			close(pipe_1[0]);74			ret = write(pipe_1[1], &tmp, 1);75			if (ret != 1) {76				ksft_print_msg(77					"Writing to pipe returned %d", ret);78				exit_code = EXIT_FAILURE;79			}80			close(pipe_1[1]);81			close(pipe_2[1]);82			ret = read(pipe_2[0], &tmp, 1);83			if (ret != 1) {84				ksft_print_msg(85					"Reading from pipe returned %d", ret);86				exit_code = EXIT_FAILURE;87			}88			close(pipe_2[0]);89		}90 91		if (set_tid[0] != getpid())92			child_exit(EXIT_FAILURE);93		child_exit(exit_code);94	}95 96	if (expected_pid == 0 || expected_pid == pid) {97		ksft_print_msg("I am the parent (%d). My child's pid is %d\n",98			       getpid(), pid);99	} else {100		ksft_print_msg(101			"Expected child pid %d does not match actual pid %d\n",102			expected_pid, pid);103		return -1;104	}105 106	if (waitpid(pid, &status, 0) < 0) {107		ksft_print_msg("Child returned %s\n", strerror(errno));108		return -errno;109	}110 111	if (!WIFEXITED(status))112		return -1;113 114	return WEXITSTATUS(status);115}116 117static void test_clone3_set_tid(const char *desc,118				pid_t *set_tid,119				size_t set_tid_size,120				int flags,121				int expected,122				int expected_pid,123				bool wait_for_it)124{125	int ret;126 127	ksft_print_msg(128		"[%d] Trying clone3() with CLONE_SET_TID to %d and 0x%x\n",129		getpid(), set_tid[0], flags);130	ret = call_clone3_set_tid(set_tid, set_tid_size, flags, expected_pid,131				  wait_for_it);132	ksft_print_msg(133		"[%d] clone3() with CLONE_SET_TID %d says: %d - expected %d\n",134		getpid(), set_tid[0], ret, expected);135 136	ksft_test_result(ret == expected, "%s with %zu TIDs and flags 0x%x\n",137			 desc, set_tid_size, flags);138}139 140int main(int argc, char *argv[])141{142	FILE *f;143	char buf;144	char *line;145	int status;146	int ret = -1;147	size_t len = 0;148	int pid_max = 0;149	uid_t uid = getuid();150	char proc_path[100] = {0};151	pid_t pid, ns1, ns2, ns3, ns_pid;152	pid_t set_tid[MAX_PID_NS_LEVEL * 2];153 154	ksft_print_header();155	ksft_set_plan(29);156	test_clone3_supported();157 158	if (pipe(pipe_1) < 0 || pipe(pipe_2) < 0)159		ksft_exit_fail_msg("pipe() failed\n");160 161	f = fopen("/proc/sys/kernel/pid_max", "r");162	if (f == NULL)163		ksft_exit_fail_msg(164			"%s - Could not open /proc/sys/kernel/pid_max\n",165			strerror(errno));166	fscanf(f, "%d", &pid_max);167	fclose(f);168	ksft_print_msg("/proc/sys/kernel/pid_max %d\n", pid_max);169 170	/* Try invalid settings */171	memset(&set_tid, 0, sizeof(set_tid));172	test_clone3_set_tid("invalid size, 0 TID",173			    set_tid, MAX_PID_NS_LEVEL + 1, 0, -EINVAL, 0, 0);174 175	test_clone3_set_tid("invalid size, 0 TID",176			    set_tid, MAX_PID_NS_LEVEL * 2, 0, -EINVAL, 0, 0);177 178	test_clone3_set_tid("invalid size, 0 TID",179			    set_tid, MAX_PID_NS_LEVEL * 2 + 1, 0,180			    -EINVAL, 0, 0);181 182	test_clone3_set_tid("invalid size, 0 TID",183			    set_tid, MAX_PID_NS_LEVEL * 42, 0, -EINVAL, 0, 0);184 185	/*186	 * This can actually work if this test running in a MAX_PID_NS_LEVEL - 1187	 * nested PID namespace.188	 */189	test_clone3_set_tid("invalid size, 0 TID",190			    set_tid, MAX_PID_NS_LEVEL - 1, 0, -EINVAL, 0, 0);191 192	memset(&set_tid, 0xff, sizeof(set_tid));193	test_clone3_set_tid("invalid size, TID all 1s",194			    set_tid, MAX_PID_NS_LEVEL + 1, 0, -EINVAL, 0, 0);195 196	test_clone3_set_tid("invalid size, TID all 1s",197			    set_tid, MAX_PID_NS_LEVEL * 2, 0, -EINVAL, 0, 0);198 199	test_clone3_set_tid("invalid size, TID all 1s",200			    set_tid, MAX_PID_NS_LEVEL * 2 + 1, 0,201			    -EINVAL, 0, 0);202 203	test_clone3_set_tid("invalid size, TID all 1s",204			    set_tid, MAX_PID_NS_LEVEL * 42, 0, -EINVAL, 0, 0);205 206	/*207	 * This can actually work if this test running in a MAX_PID_NS_LEVEL - 1208	 * nested PID namespace.209	 */210	test_clone3_set_tid("invalid size, TID all 1s",211			    set_tid, MAX_PID_NS_LEVEL - 1, 0, -EINVAL, 0, 0);212 213	memset(&set_tid, 0, sizeof(set_tid));214	/* Try with an invalid PID */215	set_tid[0] = 0;216	test_clone3_set_tid("valid size, 0 TID",217			    set_tid, 1, 0, -EINVAL, 0, 0);218 219	set_tid[0] = -1;220	test_clone3_set_tid("valid size, -1 TID",221			    set_tid, 1, 0, -EINVAL, 0, 0);222 223	/* Claim that the set_tid array actually contains 2 elements. */224	test_clone3_set_tid("2 TIDs, -1 and 0",225			    set_tid, 2, 0, -EINVAL, 0, 0);226 227	/* Try it in a new PID namespace */228	if (uid == 0)229		test_clone3_set_tid("valid size, -1 TID",230				    set_tid, 1, CLONE_NEWPID, -EINVAL, 0, 0);231	else232		ksft_test_result_skip("Clone3() with set_tid requires root\n");233 234	/* Try with a valid PID (1) this should return -EEXIST. */235	set_tid[0] = 1;236	if (uid == 0)237		test_clone3_set_tid("duplicate PID 1",238				    set_tid, 1, 0, -EEXIST, 0, 0);239	else240		ksft_test_result_skip("Clone3() with set_tid requires root\n");241 242	/* Try it in a new PID namespace */243	if (uid == 0)244		test_clone3_set_tid("duplicate PID 1",245				    set_tid, 1, CLONE_NEWPID, 0, 0, 0);246	else247		ksft_test_result_skip("Clone3() with set_tid requires root\n");248 249	/* pid_max should fail everywhere */250	set_tid[0] = pid_max;251	test_clone3_set_tid("set TID to maximum",252			    set_tid, 1, 0, -EINVAL, 0, 0);253 254	if (uid == 0)255		test_clone3_set_tid("set TID to maximum",256				    set_tid, 1, CLONE_NEWPID, -EINVAL, 0, 0);257	else258		ksft_test_result_skip("Clone3() with set_tid requires root\n");259 260	if (uid != 0) {261		/*262		 * All remaining tests require root. Tell the framework263		 * that all those tests are skipped as non-root.264		 */265		ksft_cnt.ksft_xskip += ksft_plan - ksft_test_num();266		goto out;267	}268 269	/* Find the current active PID */270	pid = fork();271	if (pid == 0) {272		ksft_print_msg("Child has PID %d\n", getpid());273		child_exit(EXIT_SUCCESS);274	}275	if (waitpid(pid, &status, 0) < 0)276		ksft_exit_fail_msg("Waiting for child %d failed", pid);277 278	/* After the child has finished, its PID should be free. */279	set_tid[0] = pid;280	test_clone3_set_tid("reallocate child TID",281			    set_tid, 1, 0, 0, 0, 0);282 283	/* This should fail as there is no PID 1 in that namespace */284	test_clone3_set_tid("duplicate child TID",285			    set_tid, 1, CLONE_NEWPID, -EINVAL, 0, 0);286 287	/*288	 * Creating a process with PID 1 in the newly created most nested289	 * PID namespace and PID 'pid' in the parent PID namespace. This290	 * needs to work.291	 */292	set_tid[0] = 1;293	set_tid[1] = pid;294	test_clone3_set_tid("create PID 1 in new NS",295			    set_tid, 2, CLONE_NEWPID, 0, pid, 0);296 297	ksft_print_msg("unshare PID namespace\n");298	if (unshare(CLONE_NEWPID) == -1)299		ksft_exit_fail_msg("unshare(CLONE_NEWPID) failed: %s\n",300				strerror(errno));301 302	set_tid[0] = pid;303 304	/* This should fail as there is no PID 1 in that namespace */305	test_clone3_set_tid("duplicate PID 1",306			    set_tid, 1, 0, -EINVAL, 0, 0);307 308	/* Let's create a PID 1 */309	ns_pid = fork();310	if (ns_pid == 0) {311		/*312		 * This and the next test cases check that all pid-s are313		 * released on error paths.314		 */315		set_tid[0] = 43;316		set_tid[1] = -1;317		test_clone3_set_tid("check leak on invalid TID -1",318				    set_tid, 2, 0, -EINVAL, 0, 0);319 320		set_tid[0] = 43;321		set_tid[1] = pid;322		test_clone3_set_tid("check leak on invalid specific TID",323				    set_tid, 2, 0, 0, 43, 0);324 325		ksft_print_msg("Child in PID namespace has PID %d\n", getpid());326		set_tid[0] = 2;327		test_clone3_set_tid("create PID 2 in child NS",328				    set_tid, 1, 0, 0, 2, 0);329 330		set_tid[0] = 1;331		set_tid[1] = -1;332		set_tid[2] = pid;333		/* This should fail as there is invalid PID at level '1'. */334		test_clone3_set_tid("fail due to invalid TID at level 1",335				    set_tid, 3, CLONE_NEWPID, -EINVAL, 0, 0);336 337		set_tid[0] = 1;338		set_tid[1] = 42;339		set_tid[2] = pid;340		/*341		 * This should fail as there are not enough active PID342		 * namespaces. Again assuming this is running in the host's343		 * PID namespace. Not yet nested.344		 */345		test_clone3_set_tid("fail due to too few active PID NSs",346				    set_tid, 4, CLONE_NEWPID, -EINVAL, 0, 0);347 348		/*349		 * This should work and from the parent we should see350		 * something like 'NSpid:	pid	42	1'.351		 */352		test_clone3_set_tid("verify that we have 3 PID NSs",353				    set_tid, 3, CLONE_NEWPID, 0, 42, true);354 355		child_exit(ksft_cnt.ksft_fail);356	}357 358	close(pipe_1[1]);359	close(pipe_2[0]);360	while (read(pipe_1[0], &buf, 1) > 0) {361		ksft_print_msg("[%d] Child is ready and waiting\n", getpid());362		break;363	}364 365	snprintf(proc_path, sizeof(proc_path), "/proc/%d/status", pid);366	f = fopen(proc_path, "r");367	if (f == NULL)368		ksft_exit_fail_msg(369			"%s - Could not open %s\n",370			strerror(errno), proc_path);371 372	while (getline(&line, &len, f) != -1) {373		if (strstr(line, "NSpid")) {374			int i;375 376			/* Verify that all generated PIDs are as expected. */377			i = sscanf(line, "NSpid:\t%d\t%d\t%d",378				   &ns3, &ns2, &ns1);379			if (i != 3) {380				ksft_print_msg(381					"Unexpected 'NSPid:' entry: %s",382					line);383				ns1 = ns2 = ns3 = 0;384			}385			break;386		}387	}388	fclose(f);389	free(line);390	close(pipe_2[0]);391 392	/* Tell the clone3()'d child to finish. */393	write(pipe_2[1], &buf, 1);394	close(pipe_2[1]);395 396	if (waitpid(ns_pid, &status, 0) < 0) {397		ksft_print_msg("Child returned %s\n", strerror(errno));398		ret = -errno;399		goto out;400	}401 402	if (!WIFEXITED(status))403		ksft_test_result_fail("Child error\n");404 405	ksft_cnt.ksft_pass += 6 - (ksft_cnt.ksft_fail - WEXITSTATUS(status));406	ksft_cnt.ksft_fail = WEXITSTATUS(status);407 408	ksft_print_msg("Expecting PIDs %d, 42, 1\n", pid);409	ksft_print_msg("Have PIDs in namespaces: %d, %d, %d\n", ns3, ns2, ns1);410	ksft_test_result(ns3 == pid && ns2 == 42 && ns1 == 1,411			 "PIDs in all namespaces as expected\n");412out:413	ret = 0;414 415	if (ret)416		ksft_exit_fail();417	ksft_exit_pass();418}419