• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2019 SUSE LLC
4  * Author: Christian Amann <camann@suse.com>
5  */
6 /*
7  * This test checks if the pidfd_send_signal syscall wrongfully sends
8  * a signal to a new process which inherited the PID of the actual
9  * target process.
10  * In order to do so it is necessary to start a process with a pre-
11  * determined PID. This is accomplished by writing to the
12  * /proc/sys/kernel/ns_last_pid file.
13  * By utilizing this, this test forks two children with the same PID.
14  * It is then checked, if the syscall will send a signal to the second
15  * child using the pidfd of the first one.
16  */
17 
18 #define _GNU_SOURCE
19 #include <signal.h>
20 #include <stdio.h>
21 #include <unistd.h>
22 #include "lapi/pidfd_send_signal.h"
23 #include "tst_safe_pthread.h"
24 
25 #define PIDTRIES	3
26 
27 static char *last_pid_file;
28 static int pidfd, new_pidfd;
29 static int old_inode, new_inode;
30 
get_inode_number(int fd)31 static int get_inode_number(int fd)
32 {
33 	struct stat file_stat;
34 
35 	SAFE_FSTAT(fd, &file_stat);
36 	return file_stat.st_ino;
37 }
38 
verify_pidfd_send_signal(void)39 static void verify_pidfd_send_signal(void)
40 {
41 	pid_t pid, new_pid;
42 	char pid_filename[32];
43 	char pid_str[16];
44 	int i, fail;
45 
46 	fail = 1;
47 	for (i = 0; i < PIDTRIES; i++) {
48 		pid = SAFE_FORK();
49 		if (pid == 0) {
50 			TST_CHECKPOINT_WAIT(0);
51 			return;
52 		}
53 
54 		sprintf(pid_filename, "/proc/%d", pid);
55 		pidfd = SAFE_OPEN(pid_filename, O_DIRECTORY | O_CLOEXEC);
56 		old_inode = get_inode_number(pidfd);
57 
58 		TST_CHECKPOINT_WAKE(0);
59 		tst_reap_children();
60 
61 		/* Manipulate PID for next process */
62 		sprintf(pid_str, "%d", pid - 1);
63 		SAFE_FILE_PRINTF(last_pid_file, "%s", pid_str);
64 
65 		new_pid = SAFE_FORK();
66 		if (new_pid == 0) {
67 			TST_CHECKPOINT_WAIT(0);
68 			return;
69 		}
70 
71 		if (new_pid == pid) {
72 			new_pidfd = SAFE_OPEN(pid_filename,
73 					O_DIRECTORY | O_CLOEXEC);
74 			new_inode = get_inode_number(new_pidfd);
75 			SAFE_CLOSE(new_pidfd);
76 			fail = 0;
77 			break;
78 		}
79 
80 		if (i < PIDTRIES) {
81 			tst_res(TINFO,
82 				"Failed to set correct PID, trying again...");
83 		}
84 		SAFE_CLOSE(pidfd);
85 		TST_CHECKPOINT_WAKE(0);
86 		tst_reap_children();
87 	}
88 	if (fail) {
89 		tst_brk(TBROK,
90 			"Could not set new child to same PID as the old one!");
91 	}
92 	if (old_inode == new_inode) {
93 		tst_res(TWARN,
94 			"File descriptor of new process points to the inode of the old process!");
95 	}
96 
97 	TEST(pidfd_send_signal(pidfd, SIGUSR1, NULL, 0));
98 	if (TST_RET == -1 && TST_ERR == ESRCH) {
99 		tst_res(TPASS,
100 			"Did not send signal to wrong process with same PID!");
101 	} else {
102 		tst_res(TFAIL | TTERRNO,
103 			"pidf_send_signal() ended unexpectedly - return value: %ld, error",
104 			TST_RET);
105 	}
106 	TST_CHECKPOINT_WAKE(0);
107 	tst_reap_children();
108 
109 	SAFE_CLOSE(pidfd);
110 }
111 
setup(void)112 static void setup(void)
113 {
114 	pidfd_send_signal_supported();
115 
116 	last_pid_file = "/proc/sys/kernel/ns_last_pid";
117 	if (access(last_pid_file, F_OK) == -1) {
118 		tst_brk(TCONF, "%s does not exist, cannot set PIDs",
119 			last_pid_file);
120 	}
121 }
122 
cleanup(void)123 static void cleanup(void)
124 {
125 	tst_reap_children();
126 	if (new_pidfd > 0)
127 		SAFE_CLOSE(new_pidfd);
128 	if (pidfd > 0)
129 		SAFE_CLOSE(pidfd);
130 }
131 
132 static struct tst_test test = {
133 	.test_all = verify_pidfd_send_signal,
134 	.setup = setup,
135 	.cleanup = cleanup,
136 	.needs_root = 1,
137 	.needs_checkpoints = 1,
138 	.forks_child = 1,
139 };
140