(root)/
strace-6.5/
tests-mx32/
pidns.c
       1  /*
       2   * Testing framework for PID namespace translation
       3   *
       4   * Copyright (c) 2020 Ákos Uzonyi <uzonyi.akos@gmail.com>
       5   * Copyright (c) 2020-2022 The strace developers.
       6   * All rights reserved.
       7   *
       8   * SPDX-License-Identifier: GPL-2.0-or-later
       9   */
      10  #include "tests.h"
      11  #include "pidns.h"
      12  #include <linux/nsfs.h>
      13  
      14  #include <errno.h>
      15  #include <stdio.h>
      16  #include <string.h>
      17  #include <sys/types.h>
      18  #include <signal.h>
      19  #include <stdlib.h>
      20  #include <sched.h>
      21  #include <unistd.h>
      22  #include <sys/wait.h>
      23  #include <linux/sched.h>
      24  #include <fcntl.h>
      25  #include <sys/ioctl.h>
      26  
      27  static bool pidns_translation = false;
      28  static bool pidns_unshared = false;
      29  
      30  /* Our PIDs in strace's namespace */
      31  static pid_t pidns_strace_ids[PT_COUNT];
      32  
      33  void
      34  pidns_print_leader(void)
      35  {
      36  	if (pidns_translation)
      37  		printf("%-5d ", pidns_strace_ids[PT_TID]);
      38  }
      39  
      40  const char *
      41  pidns_pid2str(enum pid_type type)
      42  {
      43  	static const char format[] = " /* %d in strace's PID NS */";
      44  	static char buf[PT_COUNT][sizeof(format) + sizeof(int) * 3];
      45  
      46  	if (type < 0 || type >= PT_COUNT)
      47  		return "";
      48  
      49  	if (!pidns_unshared || !pidns_strace_ids[type])
      50  		return "";
      51  
      52  	snprintf(buf[type], sizeof(buf[type]), format, pidns_strace_ids[type]);
      53  	return buf[type];
      54  }
      55  
      56  /**
      57   * This function is like fork, but does a few more things. It sets up the
      58   * child's PGID and SID according to the parameters. Also it fills the
      59   * pidns_strace_ids array in the child's memory with the PIDs of the child in
      60   * parent's PID namespace. In the parent it waits for the child to terminate
      61   * (but leaves the zombie to use it later as a process group). If the child
      62   * terminates with nonzero exit status, the test is failed.
      63   *
      64   * @param pgid     The process group the child should be moved to. It's expected
      65   *                 to be a PID of a zombie process (will be reaped). If
      66   *                 negative, leave the child in the process group of the parent.
      67   *                 If 0, move the process to its own process group.
      68   * @param new_sid  Whether child should be moved to a new session.
      69   */
      70  static pid_t
      71  pidns_fork(pid_t pgid, bool new_sid)
      72  {
      73  	int strace_ids_pipe[2];
      74  	if (pipe(strace_ids_pipe) < 0)
      75  		perror_msg_and_fail("pipe");
      76  
      77  	fflush(stdout);
      78  	pid_t pid = fork();
      79  	if (pid < 0)
      80  		perror_msg_and_fail("fork");
      81  
      82  	if (!pid) {
      83  		close(strace_ids_pipe[1]);
      84  
      85  		ssize_t len = read(strace_ids_pipe[0], pidns_strace_ids,
      86  				sizeof(pidns_strace_ids));
      87  		if (len < 0)
      88  			perror_msg_and_fail("read");
      89  		if (len != sizeof(pidns_strace_ids))
      90  			error_msg_and_fail("read returned < sizeof(pidns_strace_ids)");
      91  
      92  		close(strace_ids_pipe[0]);
      93  
      94  		if (pidns_strace_ids[PT_SID])
      95  			setsid();
      96  
      97  		return 0;
      98  	}
      99  
     100  	pidns_strace_ids[PT_TID] = pid;
     101  	pidns_strace_ids[PT_TGID] = pid;
     102  	pidns_strace_ids[PT_PGID] = 0;
     103  	pidns_strace_ids[PT_SID] = 0;
     104  
     105  	if (!pgid)
     106  		pgid = pid;
     107  
     108  	if (pgid > 0) {
     109  		if (setpgid(pid, pgid) < 0)
     110  			perror_msg_and_fail("setpgid");
     111  
     112  		pidns_strace_ids[PT_PGID] = pgid;
     113  	}
     114  
     115  	/* Reap group leader to test PGID decoding */
     116  	if (pgid > 0 && pgid != pid) {
     117  		int ret = waitpid(pgid, NULL, WNOHANG);
     118  		if (ret < 0)
     119  			perror_msg_and_fail("wait");
     120  		if (!ret)
     121  			error_msg_and_fail("could not reap group leader");
     122  	}
     123  
     124  	if (new_sid) {
     125  		pidns_strace_ids[PT_SID] = pid;
     126  		pidns_strace_ids[PT_PGID] = pid;
     127  	}
     128  
     129  	ssize_t len = write(strace_ids_pipe[1], pidns_strace_ids,
     130  	                     sizeof(pidns_strace_ids));
     131  	if (len < 0)
     132  		perror_msg_and_fail("write");
     133  	if (len != sizeof(pidns_strace_ids))
     134  		error_msg_and_fail("write returned < sizeof(pidns_strace_ids)");
     135  
     136  	close(strace_ids_pipe[0]);
     137  	close(strace_ids_pipe[1]);
     138  
     139  	/* WNOWAIT: leave the zombie, to be able to use it as a process group */
     140  	siginfo_t siginfo;
     141  	if (waitid(P_PID, pid, &siginfo, WEXITED | WNOWAIT) < 0)
     142  		perror_msg_and_fail("wait");
     143  	if (siginfo.si_code != CLD_EXITED || siginfo.si_status) {
     144  		if (siginfo.si_code == CLD_EXITED && siginfo.si_status == 77) {
     145  			error_msg_and_skip("child terminated with skip exit"
     146  					   " status");
     147  		} else {
     148  			error_msg_and_fail("child terminated with nonzero exit"
     149  					   " status");
     150  		}
     151  	}
     152  
     153  	return pid;
     154  }
     155  
     156  static void
     157  create_init_process(void)
     158  {
     159  	int child_pipe[2];
     160  	if (pipe(child_pipe) < 0)
     161  		perror_msg_and_fail("pipe");
     162  
     163  	pid_t pid = fork();
     164  	if (pid < 0)
     165  		perror_msg_and_fail("fork");
     166  
     167  	if (!pid) {
     168  		close(child_pipe[1]);
     169  		if (read(child_pipe[0], &child_pipe[1], sizeof(int)) != 0)
     170  			_exit(1);
     171  		_exit(0);
     172  	}
     173  
     174  	close(child_pipe[0]);
     175  }
     176  
     177  void
     178  check_ns_ioctl(void)
     179  {
     180  	int fd = open("/proc/self/ns/pid", O_RDONLY);
     181  	if (fd < 0) {
     182  		if (errno == ENOENT)
     183  			perror_msg_and_skip("opening /proc/self/ns/pid");
     184  		else
     185  			perror_msg_and_fail("opening /proc/self/ns/pid");
     186  	}
     187  
     188  	int userns_fd = ioctl(fd, NS_GET_USERNS);
     189  	if (userns_fd < 0) {
     190  		switch (errno) {
     191  		case ENOTTY:
     192  			error_msg_and_skip("NS_* ioctl commands are not "
     193  			                   "supported by the kernel");
     194  			break;
     195  		case EPERM:
     196  			error_msg_and_skip("NS_* ioctl commands are not "
     197  			                   "permitted by the kernel");
     198  			break;
     199  		default:
     200  			perror_msg_and_fail("ioctl(NS_GET_USERNS)");
     201  		}
     202  	}
     203  
     204  	close(userns_fd);
     205  	close(fd);
     206  }
     207  
     208  void
     209  pidns_test_init(void)
     210  {
     211  	pidns_translation = true;
     212  
     213  	check_ns_ioctl();
     214  
     215  	if (!pidns_fork(-1, false))
     216  		return;
     217  
     218  	/* Unshare user namespace too, so we do not need to be root */
     219  	if (unshare(CLONE_NEWUSER | CLONE_NEWPID) < 0) {
     220  		if (errno == EPERM)
     221  			perror_msg_and_skip("unshare");
     222  
     223  		perror_msg_and_fail("unshare");
     224  	}
     225  
     226  	pidns_unshared = true;
     227  
     228  	create_init_process();
     229  
     230  	if (!pidns_fork(-1, false))
     231  		return;
     232  
     233  	if (!pidns_fork(-1, true))
     234  		return;
     235  
     236  	pid_t pgid;
     237  	if (!(pgid = pidns_fork(0, false)))
     238  		return;
     239  
     240  	if (!pidns_fork(pgid, false))
     241  		return;
     242  
     243  	exit(0);
     244  }