Branch data Line data Source code
1 : : #include <unistd.h>
2 : : #include <sys/socket.h>
3 : : #include <sys/types.h>
4 : : #include <sys/eventfd.h>
5 : : #include <sys/epoll.h>
6 : : #include <sys/inotify.h>
7 : : #include <sys/signalfd.h>
8 : : #include <fcntl.h>
9 : : #include <signal.h>
10 : : #include <linux/if.h>
11 : : #include <sys/ioctl.h>
12 : : #include <termios.h>
13 : :
14 : : #include "proc_parse.h"
15 : : #include "sockets.h"
16 : : #include "crtools.h"
17 : : #include "log.h"
18 : : #include "util-net.h"
19 : : #include "syscall.h"
20 : : #include "files.h"
21 : : #include "sk-inet.h"
22 : : #include "proc_parse.h"
23 : : #include "mount.h"
24 : : #include "tty.h"
25 : :
26 : 1 : static int check_tty(void)
27 : : {
28 : 1 : int master = -1, slave = -1;
29 : 1 : const int lock = 1;
30 : : struct termios t;
31 : : char *slavename;
32 : 1 : int ret = -1;
33 : :
34 : : if (ARRAY_SIZE(t.c_cc) < TERMIOS_NCC) {
35 : : pr_msg("struct termios has %d @c_cc while "
36 : : "at least %d expected.\n",
37 : : (int)ARRAY_SIZE(t.c_cc),
38 : : TERMIOS_NCC);
39 : : goto out;
40 : : }
41 : :
42 : 1 : master = open("/dev/ptmx", O_RDWR);
43 [ - + ]: 1 : if (master < 0) {
44 : 0 : pr_msg("Can't open master pty.\n");
45 : 0 : goto out;
46 : : }
47 : :
48 [ - + ]: 1 : if (ioctl(master, TIOCSPTLCK, &lock)) {
49 : 0 : pr_msg("Unable to lock pty device.\n");
50 : 0 : goto out;
51 : : }
52 : :
53 : 1 : slavename = ptsname(master);
54 : 1 : slave = open(slavename, O_RDWR);
55 [ + - ]: 1 : if (slave < 0) {
56 [ - + ]: 1 : if (errno != EIO) {
57 : 0 : pr_msg("Unexpected error code on locked pty.\n");
58 : 0 : goto out;
59 : : }
60 : : } else {
61 : 0 : pr_msg("Managed to open locked pty.\n");
62 : 0 : goto out;
63 : : }
64 : :
65 : : ret = 0;
66 : : out:
67 : 1 : close_safe(&master);
68 : 1 : close_safe(&slave);
69 : 1 : return ret;
70 : : }
71 : :
72 : 1 : static int check_map_files(void)
73 : : {
74 : : int ret;
75 : :
76 : 1 : ret = access("/proc/self/map_files", R_OK);
77 [ - + ]: 1 : if (!ret)
78 : : return 0;
79 : :
80 : 0 : pr_msg("/proc/<pid>/map_files directory is missing.\n");
81 : 1 : return -1;
82 : : }
83 : :
84 : 1 : static int check_sock_diag(void)
85 : : {
86 : : int ret;
87 : :
88 : 1 : ret = collect_sockets(getpid());
89 [ - + ]: 1 : if (!ret)
90 : : return 0;
91 : :
92 : 0 : pr_msg("sock diag infrastructure is incomplete.\n");
93 : 1 : return -1;
94 : : }
95 : :
96 : 1 : static int check_ns_last_pid(void)
97 : : {
98 : : int ret;
99 : :
100 : 1 : ret = access(LAST_PID_PATH, W_OK);
101 [ - + ]: 1 : if (!ret)
102 : : return 0;
103 : :
104 : 0 : pr_msg("%s sysctl is missing.\n", LAST_PID_PATH);
105 : 1 : return -1;
106 : : }
107 : :
108 : 1 : static int check_sock_peek_off(void)
109 : : {
110 : : int sk;
111 : : int ret, off, sz;
112 : :
113 : 1 : sk = socket(PF_UNIX, SOCK_DGRAM, 0);
114 [ - + ]: 1 : if (sk < 0) {
115 : 0 : pr_perror("Can't create unix socket for check");
116 : : return -1;
117 : : }
118 : :
119 : 1 : sz = sizeof(off);
120 : 1 : ret = getsockopt(sk, SOL_SOCKET, SO_PEEK_OFF, &off, (socklen_t *)&sz);
121 : 1 : close(sk);
122 : :
123 [ + - ][ - + ]: 1 : if ((ret == 0) && (off == -1) && (sz == sizeof(int)))
[ + - ]
124 : : return 0;
125 : :
126 : 1 : pr_msg("SO_PEEK_OFF sockoption doesn't work.\n");
127 : : return -1;
128 : : }
129 : :
130 : 1 : static int check_kcmp(void)
131 : : {
132 : 1 : int ret = sys_kcmp(getpid(), -1, -1, -1, -1);
133 : :
134 [ - + ]: 1 : if (ret != -ENOSYS)
135 : : return 0;
136 : :
137 : 0 : pr_msg("System call kcmp is not supported\n");
138 : 1 : return -1;
139 : : }
140 : :
141 : 1 : static int check_prctl(void)
142 : : {
143 : 1 : unsigned long user_auxv = 0;
144 : : unsigned int *tid_addr;
145 : : int ret;
146 : :
147 : 1 : ret = sys_prctl(PR_GET_TID_ADDRESS, (unsigned long)&tid_addr, 0, 0, 0);
148 [ - + ]: 1 : if (ret) {
149 : 0 : pr_msg("prctl: PR_GET_TID_ADDRESS is not supported\n");
150 : : return -1;
151 : : }
152 : :
153 : 1 : ret = sys_prctl(PR_SET_MM, PR_SET_MM_BRK, sys_brk(0), 0, 0);
154 [ - + ]: 1 : if (ret) {
155 [ # # ]: 0 : if (ret == -EPERM)
156 : 0 : pr_msg("prctl: One needs CAP_SYS_RESOURCE capability to perform testing\n");
157 : : else
158 : 0 : pr_msg("prctl: PR_SET_MM is not supported\n");
159 : : return -1;
160 : : }
161 : :
162 : 1 : ret = sys_prctl(PR_SET_MM, PR_SET_MM_EXE_FILE, -1, 0, 0);
163 [ - + ]: 1 : if (ret != -EBADF) {
164 : 0 : pr_msg("prctl: PR_SET_MM_EXE_FILE is not supported (%d)\n", ret);
165 : : return -1;
166 : : }
167 : :
168 : 1 : ret = sys_prctl(PR_SET_MM, PR_SET_MM_AUXV, (long)&user_auxv, sizeof(user_auxv), 0);
169 [ - + ]: 1 : if (ret) {
170 : 1 : pr_msg("prctl: PR_SET_MM_AUXV is not supported\n");
171 : : return -1;
172 : : }
173 : :
174 : : return 0;
175 : : }
176 : :
177 : : static int check_fcntl(void)
178 : : {
179 : : /*
180 : : * FIXME Add test for F_GETOWNER_UIDS once
181 : : * it's merged into mainline and kernel part
182 : : * settle down.
183 : : */
184 : : return 0;
185 : : }
186 : :
187 : 1 : static int check_proc_stat(void)
188 : : {
189 : : struct proc_pid_stat stat;
190 : : int ret;
191 : :
192 : 1 : ret = parse_pid_stat(getpid(), &stat);
193 [ - + ]: 1 : if (ret) {
194 : 1 : pr_msg("procfs: stat extension is not supported\n");
195 : : return -1;
196 : : }
197 : :
198 : : return 0;
199 : : }
200 : :
201 : 1 : static int check_one_fdinfo(union fdinfo_entries *e, void *arg)
202 : : {
203 : 1 : *(int *)arg = (int)e->efd.counter;
204 : 1 : return 0;
205 : : }
206 : :
207 : 1 : static int check_fdinfo_eventfd(void)
208 : : {
209 : : int fd, ret;
210 : 1 : int cnt = 13, proc_cnt = 0;
211 : :
212 : 1 : fd = eventfd(cnt, 0);
213 [ - + ]: 1 : if (fd < 0) {
214 : 0 : pr_perror("Can't make eventfd");
215 : : return -1;
216 : : }
217 : :
218 : 1 : ret = parse_fdinfo(fd, FD_TYPES__EVENTFD, check_one_fdinfo, &proc_cnt);
219 : 1 : close(fd);
220 : :
221 [ - + ]: 1 : if (ret) {
222 : 0 : pr_err("Error parsing proc fdinfo\n");
223 : : return -1;
224 : : }
225 : :
226 [ - + ]: 1 : if (proc_cnt != cnt) {
227 : 0 : pr_err("Counter mismatch (or not met) %d want %d\n",
228 : : proc_cnt, cnt);
229 : : return -1;
230 : : }
231 : :
232 : 1 : pr_info("Eventfd fdinfo works OK (%d vs %d)\n", cnt, proc_cnt);
233 : : return 0;
234 : : }
235 : :
236 : 1 : static int check_one_sfd(union fdinfo_entries *e, void *arg)
237 : : {
238 : 1 : return 0;
239 : : }
240 : :
241 : 1 : static int check_fdinfo_signalfd(void)
242 : : {
243 : : int fd, ret;
244 : : sigset_t mask;
245 : :
246 : 1 : sigemptyset(&mask);
247 : 1 : sigaddset(&mask, SIGUSR1);
248 : 1 : fd = signalfd(-1, &mask, 0);
249 [ - + ]: 1 : if (fd < 0) {
250 : 0 : pr_perror("Can't make signalfd");
251 : : return -1;
252 : : }
253 : :
254 : 1 : ret = parse_fdinfo(fd, FD_TYPES__SIGNALFD, check_one_sfd, NULL);
255 : 1 : close(fd);
256 : :
257 [ - + ]: 1 : if (ret) {
258 : 1 : pr_err("Error parsing proc fdinfo\n");
259 : : return -1;
260 : : }
261 : :
262 : : return 0;
263 : : }
264 : :
265 : 1 : static int check_one_epoll(union fdinfo_entries *e, void *arg)
266 : : {
267 : 1 : *(int *)arg = e->epl.tfd;
268 : 1 : return 0;
269 : : }
270 : :
271 : 1 : static int check_fdinfo_eventpoll(void)
272 : : {
273 : 1 : int efd, pfd[2], proc_fd = 0, ret;
274 : : struct epoll_event ev;
275 : :
276 [ - + ]: 1 : if (pipe(pfd)) {
277 : 0 : pr_perror("Can't make pipe to watch");
278 : : return -1;
279 : : }
280 : :
281 : 1 : efd = epoll_create(1);
282 [ - + ]: 1 : if (efd < 0) {
283 : 0 : pr_perror("Can't make epoll fd");
284 : : return -1;
285 : : }
286 : :
287 : 1 : memset(&ev, 0, sizeof(ev));
288 : 1 : ev.events = EPOLLIN | EPOLLOUT;
289 : :
290 [ - + ]: 1 : if (epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], &ev)) {
291 : 0 : pr_perror("Can't add epoll tfd");
292 : : return -1;
293 : : }
294 : :
295 : 1 : ret = parse_fdinfo(efd, FD_TYPES__EVENTPOLL, check_one_epoll, &proc_fd);
296 : 1 : close(efd);
297 : 1 : close(pfd[0]);
298 : 1 : close(pfd[1]);
299 : :
300 [ - + ]: 1 : if (ret) {
301 : 0 : pr_err("Error parsing proc fdinfo\n");
302 : : return -1;
303 : : }
304 : :
305 [ - + ]: 1 : if (pfd[0] != proc_fd) {
306 : 0 : pr_err("TFD mismatch (or not met) %d want %d\n",
307 : : proc_fd, pfd[0]);
308 : : return -1;
309 : : }
310 : :
311 : 1 : pr_info("Epoll fdinfo works OK (%d vs %d)\n", pfd[0], proc_fd);
312 : : return 0;
313 : : }
314 : :
315 : 1 : static int check_one_inotify(union fdinfo_entries *e, void *arg)
316 : : {
317 : 1 : *(int *)arg = e->ify.wd;
318 : 1 : return 0;
319 : : }
320 : :
321 : 1 : static int check_fdinfo_inotify(void)
322 : : {
323 : 1 : int ifd, wd, proc_wd = -1, ret;
324 : :
325 : 1 : ifd = inotify_init1(0);
326 [ - + ]: 1 : if (ifd < 0) {
327 : 0 : pr_perror("Can't make inotify fd");
328 : : return -1;
329 : : }
330 : :
331 : 1 : wd = inotify_add_watch(ifd, ".", IN_ALL_EVENTS);
332 [ - + ]: 1 : if (wd < 0) {
333 : 0 : pr_perror("Can't add watch");
334 : : return -1;
335 : : }
336 : :
337 : 1 : ret = parse_fdinfo(ifd, FD_TYPES__INOTIFY, check_one_inotify, &proc_wd);
338 : 1 : close(ifd);
339 : :
340 [ - + ]: 1 : if (ret < 0) {
341 : 0 : pr_err("Error parsing proc fdinfo\n");
342 : : return -1;
343 : : }
344 : :
345 [ - + ]: 1 : if (wd != proc_wd) {
346 : 0 : pr_err("WD mismatch (or not met) %d want %d\n", proc_wd, wd);
347 : : return -1;
348 : : }
349 : :
350 : 1 : pr_info("Inotify fdinfo works OK (%d vs %d)\n", wd, proc_wd);
351 : : return 0;
352 : : }
353 : :
354 : 1 : static int check_fdinfo_ext(void)
355 : : {
356 : 1 : int ret = 0;
357 : :
358 : 1 : ret |= check_fdinfo_eventfd();
359 : 1 : ret |= check_fdinfo_eventpoll();
360 : 1 : ret |= check_fdinfo_signalfd();
361 : 1 : ret |= check_fdinfo_inotify();
362 : :
363 : 1 : return ret;
364 : : }
365 : :
366 : 1 : static int check_unaligned_vmsplice(void)
367 : : {
368 : : int p[2], ret;
369 : : char buf; /* :) */
370 : : struct iovec iov;
371 : :
372 : 1 : pipe(p);
373 : 1 : iov.iov_base = &buf;
374 : 1 : iov.iov_len = sizeof(buf);
375 : 1 : ret = vmsplice(p[1], &iov, 1, SPLICE_F_GIFT | SPLICE_F_NONBLOCK);
376 [ - + ]: 1 : if (ret < 0) {
377 : 0 : pr_perror("Unaligned vmsplice doesn't work");
378 : : return -1;
379 : : }
380 : :
381 : 1 : pr_info("Unaligned vmsplice works OK\n");
382 : : return 0;
383 : : }
384 : :
385 : : #ifndef SO_GET_FILTER
386 : : #define SO_GET_FILTER SO_ATTACH_FILTER
387 : : #endif
388 : :
389 : 1 : static int check_so_gets(void)
390 : : {
391 : : int sk;
392 : : socklen_t len;
393 : : char name[IFNAMSIZ];
394 : :
395 : 1 : sk = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
396 [ - + ]: 1 : if (sk < 0) {
397 : 0 : pr_perror("No socket");
398 : : return 1;
399 : : }
400 : :
401 : 1 : len = 0;
402 [ - + ]: 1 : if (getsockopt(sk, SOL_SOCKET, SO_GET_FILTER, NULL, &len)) {
403 : 0 : pr_perror("Can't get socket filter");
404 : : return 1;
405 : : }
406 : :
407 : 1 : len = sizeof(name);
408 [ - + ]: 1 : if (getsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, name, &len)) {
409 : 1 : pr_perror("Can't get socket bound dev");
410 : : return 1;
411 : : }
412 : :
413 : : return 0;
414 : : }
415 : :
416 : 1 : static int check_ipc(void)
417 : : {
418 : : int ret;
419 : :
420 : 1 : ret = access("/proc/sys/kernel/sem_next_id", R_OK | W_OK);
421 [ - + ]: 1 : if (!ret)
422 : : return 0;
423 : :
424 : 0 : pr_msg("/proc/sys/kernel/sem_next_id sysctl is missing.\n");
425 : 1 : return -1;
426 : : }
427 : :
428 : 1 : int cr_check(void)
429 : : {
430 : 1 : int ret = 0;
431 : :
432 : 1 : log_set_loglevel(LOG_ERROR);
433 : :
434 [ - + ]: 1 : if (mntns_collect_root(getpid())) {
435 : 0 : pr_err("Can't collect root mount point\n");
436 : 0 : return -1;
437 : : }
438 : :
439 : 1 : ret |= check_map_files();
440 : 1 : ret |= check_sock_diag();
441 : 1 : ret |= check_ns_last_pid();
442 : 1 : ret |= check_sock_peek_off();
443 : 1 : ret |= check_kcmp();
444 : 1 : ret |= check_prctl();
445 : 1 : ret |= check_fcntl();
446 : 1 : ret |= check_proc_stat();
447 : 1 : ret |= check_tcp_repair();
448 : 1 : ret |= check_fdinfo_ext();
449 : 1 : ret |= check_unaligned_vmsplice();
450 : 1 : ret |= check_tty();
451 : 1 : ret |= check_so_gets();
452 : 1 : ret |= check_ipc();
453 : :
454 [ + - ]: 1 : if (!ret)
455 : 1 : pr_msg("Looks good.\n");
456 : :
457 : 1 : return ret;
458 : : }
|