xref: /linux/tools/testing/selftests/pidfd/pidfd_open_test.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #define _GNU_SOURCE
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <inttypes.h>
7 #include <limits.h>
8 #include <linux/types.h>
9 #include <sched.h>
10 #include <signal.h>
11 #include <stdbool.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <syscall.h>
16 #include <sys/ioctl.h>
17 #include <sys/mount.h>
18 #include <sys/prctl.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21 
22 #include "pidfd.h"
23 #include "../kselftest.h"
24 
25 #ifndef PIDFS_IOCTL_MAGIC
26 #define PIDFS_IOCTL_MAGIC 0xFF
27 #endif
28 
29 #ifndef PIDFD_GET_INFO
30 #define PIDFD_GET_INFO _IOWR(PIDFS_IOCTL_MAGIC, 11, struct pidfd_info)
31 #define PIDFD_INFO_CGROUPID		(1UL << 0)
32 
33 struct pidfd_info {
34 	__u64 request_mask;
35 	__u64 cgroupid;
36 	__u32 pid;
37 	__u32 tgid;
38 	__u32 ppid;
39 	__u32 ruid;
40 	__u32 rgid;
41 	__u32 euid;
42 	__u32 egid;
43 	__u32 suid;
44 	__u32 sgid;
45 	__u32 fsuid;
46 	__u32 fsgid;
47 	__u32 spare0[1];
48 };
49 #endif
50 
51 static int safe_int(const char *numstr, int *converted)
52 {
53 	char *err = NULL;
54 	long sli;
55 
56 	errno = 0;
57 	sli = strtol(numstr, &err, 0);
58 	if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
59 		return -ERANGE;
60 
61 	if (errno != 0 && sli == 0)
62 		return -EINVAL;
63 
64 	if (err == numstr || *err != '\0')
65 		return -EINVAL;
66 
67 	if (sli > INT_MAX || sli < INT_MIN)
68 		return -ERANGE;
69 
70 	*converted = (int)sli;
71 	return 0;
72 }
73 
74 static int char_left_gc(const char *buffer, size_t len)
75 {
76 	size_t i;
77 
78 	for (i = 0; i < len; i++) {
79 		if (buffer[i] == ' ' ||
80 		    buffer[i] == '\t')
81 			continue;
82 
83 		return i;
84 	}
85 
86 	return 0;
87 }
88 
89 static int char_right_gc(const char *buffer, size_t len)
90 {
91 	int i;
92 
93 	for (i = len - 1; i >= 0; i--) {
94 		if (buffer[i] == ' '  ||
95 		    buffer[i] == '\t' ||
96 		    buffer[i] == '\n' ||
97 		    buffer[i] == '\0')
98 			continue;
99 
100 		return i + 1;
101 	}
102 
103 	return 0;
104 }
105 
106 static char *trim_whitespace_in_place(char *buffer)
107 {
108 	buffer += char_left_gc(buffer, strlen(buffer));
109 	buffer[char_right_gc(buffer, strlen(buffer))] = '\0';
110 	return buffer;
111 }
112 
113 static pid_t get_pid_from_fdinfo_file(int pidfd, const char *key, size_t keylen)
114 {
115 	int ret;
116 	char path[512];
117 	FILE *f;
118 	size_t n = 0;
119 	pid_t result = -1;
120 	char *line = NULL;
121 
122 	snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", pidfd);
123 
124 	f = fopen(path, "re");
125 	if (!f)
126 		return -1;
127 
128 	while (getline(&line, &n, f) != -1) {
129 		char *numstr;
130 
131 		if (strncmp(line, key, keylen))
132 			continue;
133 
134 		numstr = trim_whitespace_in_place(line + 4);
135 		ret = safe_int(numstr, &result);
136 		if (ret < 0)
137 			goto out;
138 
139 		break;
140 	}
141 
142 out:
143 	free(line);
144 	fclose(f);
145 	return result;
146 }
147 
148 int main(int argc, char **argv)
149 {
150 	struct pidfd_info info = {
151 		.request_mask = PIDFD_INFO_CGROUPID,
152 	};
153 	int pidfd = -1, ret = 1;
154 	pid_t pid;
155 
156 	ksft_set_plan(4);
157 
158 	pidfd = sys_pidfd_open(-1, 0);
159 	if (pidfd >= 0) {
160 		ksft_print_msg(
161 			"%s - succeeded to open pidfd for invalid pid -1\n",
162 			strerror(errno));
163 		goto on_error;
164 	}
165 	ksft_test_result_pass("do not allow invalid pid test: passed\n");
166 
167 	pidfd = sys_pidfd_open(getpid(), 1);
168 	if (pidfd >= 0) {
169 		ksft_print_msg(
170 			"%s - succeeded to open pidfd with invalid flag value specified\n",
171 			strerror(errno));
172 		goto on_error;
173 	}
174 	ksft_test_result_pass("do not allow invalid flag test: passed\n");
175 
176 	pidfd = sys_pidfd_open(getpid(), 0);
177 	if (pidfd < 0) {
178 		ksft_print_msg("%s - failed to open pidfd\n", strerror(errno));
179 		goto on_error;
180 	}
181 	ksft_test_result_pass("open a new pidfd test: passed\n");
182 
183 	pid = get_pid_from_fdinfo_file(pidfd, "Pid:", sizeof("Pid:") - 1);
184 	ksft_print_msg("pidfd %d refers to process with pid %d\n", pidfd, pid);
185 
186 	if (ioctl(pidfd, PIDFD_GET_INFO, &info) < 0) {
187 		ksft_print_msg("%s - failed to get info from pidfd\n", strerror(errno));
188 		goto on_error;
189 	}
190 	if (info.pid != pid) {
191 		ksft_print_msg("pid from fdinfo file %d does not match pid from ioctl %d\n",
192 			       pid, info.pid);
193 		goto on_error;
194 	}
195 	if (info.ppid != getppid()) {
196 		ksft_print_msg("ppid %d does not match ppid from ioctl %d\n",
197 			       pid, info.pid);
198 		goto on_error;
199 	}
200 	if (info.ruid != getuid()) {
201 		ksft_print_msg("uid %d does not match uid from ioctl %d\n",
202 			       getuid(), info.ruid);
203 		goto on_error;
204 	}
205 	if (info.rgid != getgid()) {
206 		ksft_print_msg("gid %d does not match gid from ioctl %d\n",
207 			       getgid(), info.rgid);
208 		goto on_error;
209 	}
210 	if (info.euid != geteuid()) {
211 		ksft_print_msg("euid %d does not match euid from ioctl %d\n",
212 			       geteuid(), info.euid);
213 		goto on_error;
214 	}
215 	if (info.egid != getegid()) {
216 		ksft_print_msg("egid %d does not match egid from ioctl %d\n",
217 			       getegid(), info.egid);
218 		goto on_error;
219 	}
220 	if (info.suid != geteuid()) {
221 		ksft_print_msg("suid %d does not match suid from ioctl %d\n",
222 			       geteuid(), info.suid);
223 		goto on_error;
224 	}
225 	if (info.sgid != getegid()) {
226 		ksft_print_msg("sgid %d does not match sgid from ioctl %d\n",
227 			       getegid(), info.sgid);
228 		goto on_error;
229 	}
230 	if ((info.request_mask & PIDFD_INFO_CGROUPID) && info.cgroupid == 0) {
231 		ksft_print_msg("cgroupid should not be 0 when PIDFD_INFO_CGROUPID is set\n");
232 		goto on_error;
233 	}
234 	ksft_test_result_pass("get info from pidfd test: passed\n");
235 
236 	ret = 0;
237 
238 on_error:
239 	if (pidfd >= 0)
240 		close(pidfd);
241 
242 	if (ret)
243 		ksft_exit_fail();
244 	ksft_exit_pass();
245 }
246