2012-03-02 14:01:57 +04:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/socket.h>
|
2012-04-05 15:43:00 +04:00
|
|
|
#include <sys/types.h>
|
2012-07-11 09:45:32 +04:00
|
|
|
#include <sys/eventfd.h>
|
2012-07-11 09:55:10 +04:00
|
|
|
#include <sys/epoll.h>
|
2012-07-11 10:26:46 +04:00
|
|
|
#include <sys/inotify.h>
|
2012-08-02 12:27:45 +04:00
|
|
|
#include <sys/signalfd.h>
|
2012-04-05 15:43:00 +04:00
|
|
|
#include <fcntl.h>
|
2012-08-02 12:27:45 +04:00
|
|
|
#include <signal.h>
|
2012-04-05 15:43:00 +04:00
|
|
|
#include "proc_parse.h"
|
2012-03-02 14:01:57 +04:00
|
|
|
#include "sockets.h"
|
2012-03-02 14:01:08 +04:00
|
|
|
#include "crtools.h"
|
|
|
|
#include "log.h"
|
2012-03-02 14:01:57 +04:00
|
|
|
#include "util-net.h"
|
2012-04-05 14:29:00 +04:00
|
|
|
#include "syscall.h"
|
2012-05-03 15:21:37 +04:00
|
|
|
#include "files.h"
|
|
|
|
#include "sk-inet.h"
|
2012-07-11 09:45:32 +04:00
|
|
|
#include "proc_parse.h"
|
2012-03-02 14:01:57 +04:00
|
|
|
|
|
|
|
static int check_map_files(void)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = access("/proc/self/map_files", R_OK);
|
|
|
|
if (!ret)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pr_msg("/proc/<pid>/map_files directory is missing.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_sock_diag(void)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2012-08-11 13:12:43 +04:00
|
|
|
ret = collect_sockets(getpid());
|
2012-03-02 14:01:57 +04:00
|
|
|
if (!ret)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pr_msg("sock diag infrastructure is incomplete.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_ns_last_pid(void)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = access(LAST_PID_PATH, W_OK);
|
|
|
|
if (!ret)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pr_msg("%s sysctl is missing.\n", LAST_PID_PATH);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_sock_peek_off(void)
|
|
|
|
{
|
|
|
|
int sk;
|
|
|
|
int ret, off, sz;
|
|
|
|
|
|
|
|
sk = socket(PF_UNIX, SOCK_DGRAM, 0);
|
|
|
|
if (sk < 0) {
|
|
|
|
pr_perror("Can't create unix socket for check");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-24 14:02:31 +04:00
|
|
|
sz = sizeof(off);
|
2012-03-02 14:01:57 +04:00
|
|
|
ret = getsockopt(sk, SOL_SOCKET, SO_PEEK_OFF, &off, (socklen_t *)&sz);
|
|
|
|
close(sk);
|
|
|
|
|
|
|
|
if ((ret == 0) && (off == -1) && (sz == sizeof(int)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pr_msg("SO_PEEK_OFF sockoption doesn't work.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2012-03-02 14:01:08 +04:00
|
|
|
|
2012-04-05 14:29:00 +04:00
|
|
|
static int check_kcmp(void)
|
|
|
|
{
|
|
|
|
int ret = sys_kcmp(getpid(), -1, -1, -1, -1);
|
|
|
|
|
|
|
|
if (ret != -ENOSYS)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pr_msg("System call kcmp is not supported\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-04-05 15:46:00 +04:00
|
|
|
static int check_prctl(void)
|
|
|
|
{
|
2012-04-05 15:43:00 +04:00
|
|
|
unsigned long user_auxv = 0;
|
2012-04-05 15:46:00 +04:00
|
|
|
unsigned int *tid_addr;
|
|
|
|
int ret;
|
|
|
|
|
2012-04-25 21:59:00 +04:00
|
|
|
ret = sys_prctl(PR_GET_TID_ADDRESS, (unsigned long)&tid_addr, 0, 0, 0);
|
2012-04-05 15:46:00 +04:00
|
|
|
if (ret) {
|
2012-04-25 21:59:00 +04:00
|
|
|
pr_msg("prctl: PR_GET_TID_ADDRESS is not supported\n");
|
2012-04-05 15:46:00 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = sys_prctl(PR_SET_MM, PR_SET_MM_BRK, sys_brk(0), 0, 0);
|
|
|
|
if (ret) {
|
2012-04-05 16:53:53 +04:00
|
|
|
if (ret == -EPERM)
|
|
|
|
pr_msg("prctl: One needs CAP_SYS_RESOURCE capability to perform testing\n");
|
|
|
|
else
|
|
|
|
pr_msg("prctl: PR_SET_MM is not supported\n");
|
2012-04-05 15:46:00 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = sys_prctl(PR_SET_MM, PR_SET_MM_EXE_FILE, -1, 0, 0);
|
2012-07-11 09:46:44 +04:00
|
|
|
if (ret != -EBADF) {
|
|
|
|
pr_msg("prctl: PR_SET_MM_EXE_FILE is not supported (%d)\n", ret);
|
2012-04-05 15:46:00 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-04-05 15:43:00 +04:00
|
|
|
ret = sys_prctl(PR_SET_MM, PR_SET_MM_AUXV, (long)&user_auxv, sizeof(user_auxv), 0);
|
|
|
|
if (ret) {
|
|
|
|
pr_msg("prctl: PR_SET_MM_AUXV is not supported\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-04-05 15:46:00 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-05 15:43:00 +04:00
|
|
|
static int check_fcntl(void)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* FIXME Add test for F_GETOWNER_UIDS once
|
|
|
|
* it's merged into mainline and kernel part
|
|
|
|
* settle down.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-05 15:43:00 +04:00
|
|
|
static int check_proc_stat(void)
|
|
|
|
{
|
|
|
|
struct proc_pid_stat stat;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = parse_pid_stat(getpid(), &stat);
|
|
|
|
if (ret) {
|
|
|
|
pr_msg("procfs: stat extension is not supported\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-11 09:45:32 +04:00
|
|
|
static int check_one_fdinfo(union fdinfo_entries *e, void *arg)
|
|
|
|
{
|
|
|
|
*(int *)arg = (int)e->efd.counter;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_fdinfo_eventfd(void)
|
|
|
|
{
|
|
|
|
int fd, ret;
|
|
|
|
int cnt = 13, proc_cnt = 0;
|
|
|
|
|
|
|
|
fd = eventfd(cnt, 0);
|
|
|
|
if (fd < 0) {
|
|
|
|
pr_perror("Can't make eventfd");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-19 10:18:37 +04:00
|
|
|
ret = parse_fdinfo(fd, FD_TYPES__EVENTFD, check_one_fdinfo, &proc_cnt);
|
2012-07-11 09:45:32 +04:00
|
|
|
close(fd);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Error parsing proc fdinfo\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (proc_cnt != cnt) {
|
|
|
|
pr_err("Counter mismatch (or not met) %d want %d\n",
|
|
|
|
proc_cnt, cnt);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_info("Eventfd fdinfo works OK (%d vs %d)\n", cnt, proc_cnt);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-02 12:27:45 +04:00
|
|
|
static int check_one_sfd(union fdinfo_entries *e, void *arg)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_fdinfo_signalfd(void)
|
|
|
|
{
|
|
|
|
int fd, ret;
|
|
|
|
sigset_t mask;
|
|
|
|
|
|
|
|
sigemptyset(&mask);
|
|
|
|
sigaddset(&mask, SIGUSR1);
|
|
|
|
fd = signalfd(-1, &mask, 0);
|
|
|
|
if (fd < 0) {
|
|
|
|
pr_perror("Can't make signalfd");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = parse_fdinfo(fd, FD_TYPES__SIGNALFD, check_one_sfd, NULL);
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Error parsing proc fdinfo\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-11 09:55:10 +04:00
|
|
|
static int check_one_epoll(union fdinfo_entries *e, void *arg)
|
|
|
|
{
|
|
|
|
*(int *)arg = e->epl.tfd;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_fdinfo_eventpoll(void)
|
|
|
|
{
|
|
|
|
int efd, pfd[2], proc_fd = 0, ret;
|
|
|
|
struct epoll_event ev;
|
|
|
|
|
|
|
|
if (pipe(pfd)) {
|
|
|
|
pr_perror("Can't make pipe to watch");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
efd = epoll_create(1);
|
|
|
|
if (efd < 0) {
|
|
|
|
pr_perror("Can't make epoll fd");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&ev, 0, sizeof(ev));
|
|
|
|
ev.events = EPOLLIN | EPOLLOUT;
|
|
|
|
|
|
|
|
if (epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], &ev)) {
|
|
|
|
pr_perror("Can't add epoll tfd");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-19 10:18:37 +04:00
|
|
|
ret = parse_fdinfo(efd, FD_TYPES__EVENTPOLL, check_one_epoll, &proc_fd);
|
2012-07-11 09:55:10 +04:00
|
|
|
close(efd);
|
|
|
|
close(pfd[0]);
|
|
|
|
close(pfd[1]);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Error parsing proc fdinfo");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pfd[0] != proc_fd) {
|
|
|
|
pr_err("TFD mismatch (or not met) %d want %d\n",
|
|
|
|
proc_fd, pfd[0]);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_info("Epoll fdinfo works OK (%d vs %d)\n", pfd[0], proc_fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-11 10:26:46 +04:00
|
|
|
static int check_one_inotify(union fdinfo_entries *e, void *arg)
|
|
|
|
{
|
|
|
|
*(int *)arg = e->ify.wd;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_fdinfo_inotify(void)
|
|
|
|
{
|
|
|
|
int ifd, wd, proc_wd = -1, ret;
|
|
|
|
|
|
|
|
ifd = inotify_init1(0);
|
|
|
|
if (ifd < 0) {
|
|
|
|
pr_perror("Can't make inotify fd");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
wd = inotify_add_watch(ifd, ".", IN_ALL_EVENTS);
|
|
|
|
if (wd < 0) {
|
|
|
|
pr_perror("Can't add watch");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-19 10:18:37 +04:00
|
|
|
ret = parse_fdinfo(ifd, FD_TYPES__INOTIFY, check_one_inotify, &proc_wd);
|
2012-07-11 10:26:46 +04:00
|
|
|
close(ifd);
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_err("Error parsing proc fdinfo\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wd != proc_wd) {
|
|
|
|
pr_err("WD mismatch (or not met) %d want %d\n", proc_wd, wd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_info("Inotify fdinfo works OK (%d vs %d)\n", wd, proc_wd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-11 09:45:32 +04:00
|
|
|
static int check_fdinfo_ext(void)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
ret |= check_fdinfo_eventfd();
|
2012-07-11 09:55:10 +04:00
|
|
|
ret |= check_fdinfo_eventpoll();
|
2012-08-02 12:27:45 +04:00
|
|
|
ret |= check_fdinfo_signalfd();
|
2012-07-11 10:26:46 +04:00
|
|
|
ret |= check_fdinfo_inotify();
|
2012-07-11 09:45:32 +04:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-07-12 06:57:20 +04:00
|
|
|
static int check_unaligned_vmsplice(void)
|
|
|
|
{
|
|
|
|
int p[2], ret;
|
|
|
|
char buf; /* :) */
|
|
|
|
struct iovec iov;
|
|
|
|
|
|
|
|
pipe(p);
|
|
|
|
iov.iov_base = &buf;
|
|
|
|
iov.iov_len = sizeof(buf);
|
|
|
|
ret = vmsplice(p[1], &iov, 1, SPLICE_F_GIFT | SPLICE_F_NONBLOCK);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_perror("Unaligned vmsplice doesn't work");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_info("Unaligned vmsplice works OK\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-02 14:01:08 +04:00
|
|
|
int cr_check(void)
|
|
|
|
{
|
2012-03-02 14:01:57 +04:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
ret |= check_map_files();
|
|
|
|
ret |= check_sock_diag();
|
|
|
|
ret |= check_ns_last_pid();
|
|
|
|
ret |= check_sock_peek_off();
|
2012-04-05 14:29:00 +04:00
|
|
|
ret |= check_kcmp();
|
2012-04-05 15:46:00 +04:00
|
|
|
ret |= check_prctl();
|
2012-04-05 15:43:00 +04:00
|
|
|
ret |= check_fcntl();
|
2012-04-05 15:43:00 +04:00
|
|
|
ret |= check_proc_stat();
|
2012-05-03 15:21:37 +04:00
|
|
|
ret |= check_tcp_repair();
|
2012-07-11 09:45:32 +04:00
|
|
|
ret |= check_fdinfo_ext();
|
2012-07-12 06:57:20 +04:00
|
|
|
ret |= check_unaligned_vmsplice();
|
2012-03-02 14:01:57 +04:00
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
pr_msg("Looks good.\n");
|
|
|
|
|
|
|
|
return ret;
|
2012-03-02 14:01:08 +04:00
|
|
|
}
|