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 <sys/ptrace.h>
9 : : #include <sys/wait.h>
10 : : #include <fcntl.h>
11 : : #include <signal.h>
12 : : #include <linux/if.h>
13 : : #include <sys/ioctl.h>
14 : : #include <termios.h>
15 : :
16 : : #include "proc_parse.h"
17 : : #include "sockets.h"
18 : : #include "crtools.h"
19 : : #include "log.h"
20 : : #include "util-pie.h"
21 : : #include "syscall.h"
22 : : #include "prctl.h"
23 : : #include "files.h"
24 : : #include "sk-inet.h"
25 : : #include "proc_parse.h"
26 : : #include "mount.h"
27 : : #include "tty.h"
28 : : #include "ptrace.h"
29 : : #include "kerndat.h"
30 : : #include "tun.h"
31 : : #include "namespaces.h"
32 : : #include "pstree.h"
33 : :
34 : 2 : static int check_tty(void)
35 : : {
36 : 2 : int master = -1, slave = -1;
37 : 2 : const int lock = 1;
38 : : struct termios t;
39 : : char *slavename;
40 : : int ret = -1;
41 : :
42 : : if (ARRAY_SIZE(t.c_cc) < TERMIOS_NCC) {
43 : : pr_msg("struct termios has %d @c_cc while "
44 : : "at least %d expected.\n",
45 : : (int)ARRAY_SIZE(t.c_cc),
46 : : TERMIOS_NCC);
47 : : goto out;
48 : : }
49 : :
50 : 2 : master = open("/dev/ptmx", O_RDWR);
51 [ - + ]: 2 : if (master < 0) {
52 : 0 : pr_msg("Can't open master pty.\n");
53 : 0 : goto out;
54 : : }
55 : :
56 [ - + ]: 2 : if (ioctl(master, TIOCSPTLCK, &lock)) {
57 : 0 : pr_msg("Unable to lock pty device.\n");
58 : 0 : goto out;
59 : : }
60 : :
61 : 2 : slavename = ptsname(master);
62 : 2 : slave = open(slavename, O_RDWR);
63 [ + - ]: 2 : if (slave < 0) {
64 [ - + ]: 2 : if (errno != EIO) {
65 : 0 : pr_msg("Unexpected error code on locked pty.\n");
66 : 0 : goto out;
67 : : }
68 : : } else {
69 : 0 : pr_msg("Managed to open locked pty.\n");
70 : 0 : goto out;
71 : : }
72 : :
73 : : ret = 0;
74 : : out:
75 : 2 : close_safe(&master);
76 : 2 : close_safe(&slave);
77 : 2 : return ret;
78 : : }
79 : :
80 : 2 : static int check_map_files(void)
81 : : {
82 : : int ret;
83 : :
84 : 2 : ret = access("/proc/self/map_files", R_OK);
85 [ - + ]: 2 : if (!ret)
86 : : return 0;
87 : :
88 : 0 : pr_msg("/proc/<pid>/map_files directory is missing.\n");
89 : 0 : return -1;
90 : : }
91 : :
92 : 2 : static int check_sock_diag(void)
93 : : {
94 : : int ret;
95 : :
96 : 2 : ret = collect_sockets(0);
97 [ - + ]: 2 : if (!ret)
98 : : return 0;
99 : :
100 : 0 : pr_msg("The sock diag infrastructure is incomplete.\n");
101 : 0 : pr_msg("Make sure you have:\n");
102 : 0 : pr_msg(" 1. *_DIAG kernel config options turned on;\n");
103 : 0 : pr_msg(" 2. *_diag.ko modules loaded (if compiled as modules).\n");
104 : 0 : return -1;
105 : : }
106 : :
107 : 2 : static int check_ns_last_pid(void)
108 : : {
109 : : int ret;
110 : :
111 : 2 : ret = access(LAST_PID_PATH, W_OK);
112 [ - + ]: 2 : if (!ret)
113 : : return 0;
114 : :
115 : 0 : pr_msg("%s sysctl is missing.\n", LAST_PID_PATH);
116 : 0 : return -1;
117 : : }
118 : :
119 : 2 : static int check_sock_peek_off(void)
120 : : {
121 : : int sk;
122 : : int ret, off, sz;
123 : :
124 : 2 : sk = socket(PF_UNIX, SOCK_DGRAM, 0);
125 [ - + ]: 2 : if (sk < 0) {
126 : 0 : pr_perror("Can't create unix socket for check");
127 : 0 : return -1;
128 : : }
129 : :
130 : 2 : sz = sizeof(off);
131 : 2 : ret = getsockopt(sk, SOL_SOCKET, SO_PEEK_OFF, &off, (socklen_t *)&sz);
132 : 2 : close(sk);
133 : :
134 [ + - ][ + - ]: 2 : if ((ret == 0) && (off == -1) && (sz == sizeof(int)))
[ - + ]
135 : : return 0;
136 : :
137 : 0 : pr_msg("SO_PEEK_OFF sockoption doesn't work.\n");
138 : 0 : return -1;
139 : : }
140 : :
141 : 2 : static int check_kcmp(void)
142 : : {
143 : 2 : int ret = sys_kcmp(getpid(), -1, -1, -1, -1);
144 : :
145 [ - + ]: 2 : if (ret != -ENOSYS)
146 : : return 0;
147 : :
148 : 0 : pr_msg("System call kcmp is not supported\n");
149 : 0 : return -1;
150 : : }
151 : :
152 : 2 : static int check_prctl(void)
153 : : {
154 : 2 : unsigned long user_auxv = 0;
155 : : unsigned int *tid_addr;
156 : : int ret;
157 : :
158 : 2 : ret = sys_prctl(PR_GET_TID_ADDRESS, (unsigned long)&tid_addr, 0, 0, 0);
159 [ - + ]: 2 : if (ret) {
160 : 0 : pr_msg("prctl: PR_GET_TID_ADDRESS is not supported\n");
161 : 0 : return -1;
162 : : }
163 : :
164 : 2 : ret = sys_prctl(PR_SET_MM, PR_SET_MM_BRK, sys_brk(0), 0, 0);
165 [ - + ]: 2 : if (ret) {
166 [ # # ]: 0 : if (ret == -EPERM)
167 : 0 : pr_msg("prctl: One needs CAP_SYS_RESOURCE capability to perform testing\n");
168 : : else
169 : 0 : pr_msg("prctl: PR_SET_MM is not supported\n");
170 : : return -1;
171 : : }
172 : :
173 : 2 : ret = sys_prctl(PR_SET_MM, PR_SET_MM_EXE_FILE, -1, 0, 0);
174 [ - + ]: 2 : if (ret != -EBADF) {
175 : 0 : pr_msg("prctl: PR_SET_MM_EXE_FILE is not supported (%d)\n", ret);
176 : 0 : return -1;
177 : : }
178 : :
179 : 2 : ret = sys_prctl(PR_SET_MM, PR_SET_MM_AUXV, (long)&user_auxv, sizeof(user_auxv), 0);
180 [ - + ]: 2 : if (ret) {
181 : 0 : pr_msg("prctl: PR_SET_MM_AUXV is not supported\n");
182 : 0 : return -1;
183 : : }
184 : :
185 : : return 0;
186 : : }
187 : :
188 : : static int check_fcntl(void)
189 : : {
190 : : /*
191 : : * FIXME Add test for F_GETOWNER_UIDS once
192 : : * it's merged into mainline and kernel part
193 : : * settle down.
194 : : */
195 : : return 0;
196 : : }
197 : :
198 : 2 : static int check_proc_stat(void)
199 : : {
200 : : struct proc_pid_stat stat;
201 : : int ret;
202 : :
203 : 2 : ret = parse_pid_stat(getpid(), &stat);
204 [ - + ]: 2 : if (ret) {
205 : 0 : pr_msg("procfs: stat extension is not supported\n");
206 : 0 : return -1;
207 : : }
208 : :
209 : : return 0;
210 : : }
211 : :
212 : 2 : static int check_one_fdinfo(union fdinfo_entries *e, void *arg)
213 : : {
214 : 2 : *(int *)arg = (int)e->efd.counter;
215 : 2 : return 0;
216 : : }
217 : :
218 : 2 : static int check_fdinfo_eventfd(void)
219 : : {
220 : : int fd, ret;
221 : 2 : int cnt = 13, proc_cnt = 0;
222 : :
223 : 2 : fd = eventfd(cnt, 0);
224 [ - + ]: 2 : if (fd < 0) {
225 : 0 : pr_perror("Can't make eventfd");
226 : 0 : return -1;
227 : : }
228 : :
229 : 2 : ret = parse_fdinfo(fd, FD_TYPES__EVENTFD, check_one_fdinfo, &proc_cnt);
230 : 2 : close(fd);
231 : :
232 [ - + ]: 2 : if (ret) {
233 : 0 : pr_err("Error parsing proc fdinfo\n");
234 : 0 : return -1;
235 : : }
236 : :
237 [ - + ]: 2 : if (proc_cnt != cnt) {
238 : 0 : pr_err("Counter mismatch (or not met) %d want %d\n",
239 : : proc_cnt, cnt);
240 : 0 : return -1;
241 : : }
242 : :
243 : 2 : pr_info("Eventfd fdinfo works OK (%d vs %d)\n", cnt, proc_cnt);
244 : 2 : return 0;
245 : : }
246 : :
247 : 2 : static int check_one_sfd(union fdinfo_entries *e, void *arg)
248 : : {
249 : 2 : return 0;
250 : : }
251 : :
252 : 2 : int check_mnt_id(void)
253 : : {
254 : 2 : struct fdinfo_common fdinfo = { .mnt_id = -1 };
255 : : int ret;
256 : :
257 : 2 : ret = parse_fdinfo(get_service_fd(LOG_FD_OFF), FD_TYPES__UND, NULL, &fdinfo);
258 [ + - ]: 2 : if (ret < 0)
259 : : return -1;
260 : :
261 [ + - ]: 2 : if (fdinfo.mnt_id == -1) {
262 : 2 : pr_err("fdinfo doesn't contain the mnt_id field\n");
263 : 2 : return -1;
264 : : }
265 : :
266 : : return 0;
267 : : }
268 : :
269 : 2 : static int check_fdinfo_signalfd(void)
270 : : {
271 : : int fd, ret;
272 : : sigset_t mask;
273 : :
274 : 2 : sigemptyset(&mask);
275 : 2 : sigaddset(&mask, SIGUSR1);
276 : 2 : fd = signalfd(-1, &mask, 0);
277 [ - + ]: 2 : if (fd < 0) {
278 : 0 : pr_perror("Can't make signalfd");
279 : 0 : return -1;
280 : : }
281 : :
282 : 2 : ret = parse_fdinfo(fd, FD_TYPES__SIGNALFD, check_one_sfd, NULL);
283 : 2 : close(fd);
284 : :
285 [ - + ]: 2 : if (ret) {
286 : 0 : pr_err("Error parsing proc fdinfo\n");
287 : 0 : return -1;
288 : : }
289 : :
290 : : return 0;
291 : : }
292 : :
293 : 2 : static int check_one_epoll(union fdinfo_entries *e, void *arg)
294 : : {
295 : 2 : *(int *)arg = e->epl.tfd;
296 : 2 : return 0;
297 : : }
298 : :
299 : 2 : static int check_fdinfo_eventpoll(void)
300 : : {
301 : 2 : int efd, pfd[2], proc_fd = 0, ret = -1;
302 : : struct epoll_event ev;
303 : :
304 [ - + ]: 2 : if (pipe(pfd)) {
305 : 0 : pr_perror("Can't make pipe to watch");
306 : 0 : return -1;
307 : : }
308 : :
309 : 2 : efd = epoll_create(1);
310 [ - + ]: 2 : if (efd < 0) {
311 : 0 : pr_perror("Can't make epoll fd");
312 : 0 : goto pipe_err;
313 : : }
314 : :
315 : : memset(&ev, 0, sizeof(ev));
316 : 2 : ev.events = EPOLLIN | EPOLLOUT;
317 : :
318 [ - + ]: 2 : if (epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], &ev)) {
319 : 0 : pr_perror("Can't add epoll tfd");
320 : 0 : goto epoll_err;
321 : : }
322 : :
323 : 2 : ret = parse_fdinfo(efd, FD_TYPES__EVENTPOLL, check_one_epoll, &proc_fd);
324 [ - + ]: 2 : if (ret) {
325 : 0 : pr_err("Error parsing proc fdinfo\n");
326 : 0 : goto epoll_err;
327 : : }
328 : :
329 [ - + ]: 2 : if (pfd[0] != proc_fd) {
330 : 0 : pr_err("TFD mismatch (or not met) %d want %d\n",
331 : : proc_fd, pfd[0]);
332 : : ret = -1;
333 : 0 : goto epoll_err;
334 : : }
335 : :
336 : 2 : pr_info("Epoll fdinfo works OK (%d vs %d)\n", pfd[0], proc_fd);
337 : :
338 : : epoll_err:
339 : 2 : close(efd);
340 : : pipe_err:
341 : 2 : close(pfd[0]);
342 : 2 : close(pfd[1]);
343 : :
344 : 2 : return ret;
345 : : }
346 : :
347 : 2 : static int check_one_inotify(union fdinfo_entries *e, void *arg)
348 : : {
349 : 2 : *(int *)arg = e->ify.wd;
350 : 2 : return 0;
351 : : }
352 : :
353 : 2 : static int check_fdinfo_inotify(void)
354 : : {
355 : 2 : int ifd, wd, proc_wd = -1, ret;
356 : :
357 : 2 : ifd = inotify_init1(0);
358 [ - + ]: 2 : if (ifd < 0) {
359 : 0 : pr_perror("Can't make inotify fd");
360 : 0 : return -1;
361 : : }
362 : :
363 : 2 : wd = inotify_add_watch(ifd, ".", IN_ALL_EVENTS);
364 [ - + ]: 2 : if (wd < 0) {
365 : 0 : pr_perror("Can't add watch");
366 : 0 : close(ifd);
367 : 0 : return -1;
368 : : }
369 : :
370 : 2 : ret = parse_fdinfo(ifd, FD_TYPES__INOTIFY, check_one_inotify, &proc_wd);
371 : 2 : close(ifd);
372 : :
373 [ - + ]: 2 : if (ret < 0) {
374 : 0 : pr_err("Error parsing proc fdinfo\n");
375 : 0 : return -1;
376 : : }
377 : :
378 [ - + ]: 2 : if (wd != proc_wd) {
379 : 0 : pr_err("WD mismatch (or not met) %d want %d\n", proc_wd, wd);
380 : 0 : return -1;
381 : : }
382 : :
383 : 2 : pr_info("Inotify fdinfo works OK (%d vs %d)\n", wd, proc_wd);
384 : 2 : return 0;
385 : : }
386 : :
387 : 2 : static int check_fdinfo_ext(void)
388 : : {
389 : : int ret = 0;
390 : :
391 : 2 : ret |= check_fdinfo_eventfd();
392 : 2 : ret |= check_fdinfo_eventpoll();
393 : 2 : ret |= check_fdinfo_signalfd();
394 : 2 : ret |= check_fdinfo_inotify();
395 : :
396 : 2 : return ret;
397 : : }
398 : :
399 : 2 : static int check_unaligned_vmsplice(void)
400 : : {
401 : : int p[2], ret;
402 : : char buf; /* :) */
403 : : struct iovec iov;
404 : :
405 : 2 : ret = pipe(p);
406 [ - + ]: 2 : if (ret < 0) {
407 : 0 : pr_perror("Can't create pipe");
408 : 0 : return ret;
409 : : }
410 : 2 : iov.iov_base = &buf;
411 : 2 : iov.iov_len = sizeof(buf);
412 : 2 : ret = vmsplice(p[1], &iov, 1, SPLICE_F_GIFT | SPLICE_F_NONBLOCK);
413 [ - + ]: 2 : if (ret < 0) {
414 : 0 : pr_perror("Unaligned vmsplice doesn't work");
415 : 0 : goto err;
416 : : }
417 : :
418 : 2 : pr_info("Unaligned vmsplice works OK\n");
419 : : ret = 0;
420 : : err:
421 : 2 : close(p[0]);
422 : 2 : close(p[1]);
423 : :
424 : 2 : return ret;
425 : : }
426 : :
427 : : #ifndef SO_GET_FILTER
428 : : #define SO_GET_FILTER SO_ATTACH_FILTER
429 : : #endif
430 : :
431 : 2 : static int check_so_gets(void)
432 : : {
433 : : int sk, ret = -1;
434 : : socklen_t len;
435 : : char name[IFNAMSIZ];
436 : :
437 : 2 : sk = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
438 [ - + ]: 2 : if (sk < 0) {
439 : 0 : pr_perror("No socket");
440 : 0 : return -1;
441 : : }
442 : :
443 : 2 : len = 0;
444 [ - + ]: 2 : if (getsockopt(sk, SOL_SOCKET, SO_GET_FILTER, NULL, &len)) {
445 : 0 : pr_perror("Can't get socket filter");
446 : 0 : goto err;
447 : : }
448 : :
449 : 2 : len = sizeof(name);
450 [ - + ]: 2 : if (getsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, name, &len)) {
451 : 0 : pr_perror("Can't get socket bound dev");
452 : 0 : goto err;
453 : : }
454 : :
455 : : ret = 0;
456 : : err:
457 : 2 : close(sk);
458 : 2 : return ret;
459 : : }
460 : :
461 : 2 : static int check_ipc(void)
462 : : {
463 : : int ret;
464 : :
465 : 2 : ret = access("/proc/sys/kernel/sem_next_id", R_OK | W_OK);
466 [ - + ]: 2 : if (!ret)
467 : : return 0;
468 : :
469 : 0 : pr_msg("/proc/sys/kernel/sem_next_id sysctl is missing.\n");
470 : 0 : return -1;
471 : : }
472 : :
473 : 2 : int check_sigqueuinfo()
474 : : {
475 : 2 : siginfo_t info = { .si_code = 1 };
476 : :
477 : 2 : signal(SIGUSR1, SIG_IGN);
478 : :
479 [ - + ]: 2 : if (sys_rt_sigqueueinfo(getpid(), SIGUSR1, &info)) {
480 : 0 : pr_perror("Unable to send siginfo with positive si_code to itself");
481 : 0 : return -1;
482 : : }
483 : :
484 : : return 0;
485 : : }
486 : :
487 : 2 : int check_ptrace_peeksiginfo()
488 : : {
489 : : struct ptrace_peeksiginfo_args arg;
490 : : siginfo_t siginfo;
491 : : pid_t pid, ret = 0;
492 : : k_rtsigset_t mask;
493 : :
494 : 2 : pid = fork();
495 [ - + ]: 2 : if (pid < 0)
496 : 0 : pr_perror("fork");
497 [ - + ]: 2 : else if (pid == 0) {
498 : : while (1)
499 : 0 : sleep(1000);
500 : : exit(1);
501 : : }
502 : :
503 [ + - ]: 2 : if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1)
504 : : return -1;
505 : :
506 : 2 : waitpid(pid, NULL, 0);
507 : :
508 : 2 : arg.flags = 0;
509 : 2 : arg.off = 0;
510 : 2 : arg.nr = 1;
511 : :
512 [ - + ]: 2 : if (ptrace(PTRACE_PEEKSIGINFO, pid, &arg, &siginfo) != 0) {
513 : 0 : pr_perror("Unable to dump pending signals");
514 : : ret = -1;
515 : : }
516 : :
517 [ - + ]: 2 : if (ptrace(PTRACE_GETSIGMASK, pid, sizeof(mask), &mask) != 0) {
518 : 0 : pr_perror("Unable to dump signal blocking mask");
519 : : ret = -1;
520 : : }
521 : :
522 : 2 : ptrace(PTRACE_KILL, pid, NULL, NULL);
523 : :
524 : 2 : return ret;
525 : : }
526 : :
527 : 2 : static int check_mem_dirty_track(void)
528 : : {
529 [ + - ]: 2 : if (kerndat_get_dirty_track() < 0)
530 : : return -1;
531 : :
532 [ - + ]: 2 : if (!kerndat_has_dirty_track)
533 : 0 : pr_warn("Dirty tracking is OFF. Memory snapshot will not work.\n");
534 : : return 0;
535 : : }
536 : :
537 : 2 : static int check_posix_timers(void)
538 : : {
539 : : int ret;
540 : :
541 : 2 : ret = access("/proc/self/timers", R_OK);
542 [ - + ]: 2 : if (!ret)
543 : : return 0;
544 : :
545 : 0 : pr_msg("/proc/<pid>/timers file is missing.\n");
546 : 0 : return -1;
547 : : }
548 : :
549 : 2 : int cr_check(void)
550 : : {
551 : 2 : struct ns_id ns = { .pid = getpid(), .nd = &mnt_ns_desc };
552 : : int ret = 0;
553 : :
554 : 2 : log_set_loglevel(LOG_WARN);
555 : :
556 [ + - ]: 2 : if (!is_root_user())
557 : : return -1;
558 : :
559 : 2 : root_item = alloc_pstree_item();
560 [ + - ]: 2 : if (root_item == NULL)
561 : : return -1;
562 : :
563 : 2 : root_item->pid.real = getpid();
564 : :
565 [ + - ]: 2 : if (collect_pstree_ids())
566 : : return -1;
567 : :
568 : 2 : ns.id = root_item->ids->mnt_ns_id;
569 : :
570 : 2 : mntinfo = collect_mntinfo(&ns);
571 [ + - ]: 2 : if (mntinfo == NULL)
572 : : return -1;
573 : :
574 : 2 : ret |= check_map_files();
575 : 2 : ret |= check_sock_diag();
576 : 2 : ret |= check_ns_last_pid();
577 : 2 : ret |= check_sock_peek_off();
578 : 2 : ret |= check_kcmp();
579 : 2 : ret |= check_prctl();
580 : : ret |= check_fcntl();
581 : 2 : ret |= check_proc_stat();
582 : 2 : ret |= check_tcp();
583 : 2 : ret |= check_fdinfo_ext();
584 : 2 : ret |= check_unaligned_vmsplice();
585 : 2 : ret |= check_tty();
586 : 2 : ret |= check_so_gets();
587 : 2 : ret |= check_ipc();
588 : 2 : ret |= check_sigqueuinfo();
589 : 2 : ret |= check_ptrace_peeksiginfo();
590 : 2 : ret |= check_mem_dirty_track();
591 : 2 : ret |= check_posix_timers();
592 : 2 : ret |= check_tun();
593 : 2 : ret |= check_mnt_id();
594 : :
595 [ - + ]: 2 : if (!ret)
596 : 0 : pr_msg("Looks good.\n");
597 : :
598 : 2 : return ret;
599 : : }
|