aboutgitcodelistschat:MatrixIRC
path: root/tests/unit/util.c
blob: 66448bbc9b192cc7a1f550bce19f1ed70eb85dbe (plain) (tree)






















                          
                          





                         
                                         
                     

                         
 
                                   







                                                                             
                                                              




                                                                              




                                                       



            
                    



                                         

                                                                           
                        















                                                                    
                                                       
 

                               
 





                                                             



































































                                                                           










                                                                                














                                                                     














                                                                     


                                              
                                           

 







































                                                                          





                                           

                                    
                                                                              


















                                                                                    
                     
 















                                                                                           
#define _GNU_SOURCE

#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sched.h>
#include <unistd.h>
#include <signal.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <sys/mman.h>
#include <sys/un.h>
#include <sys/socket.h>

#include "testutil.h"
#include "common/common.h"

struct seccomp_notif req;
int notifyfd;
struct args_target *at;
int pipefd[2];
pid_t pid;
char path[PATH_MAX] = "/tmp/test-seitan";
struct gluten gluten;
char stderr_buff[BUFSIZ];
char stdout_buff[BUFSIZ];

int install_single_syscall(long nr)
{
	/* filter a single syscall for the tests */
	struct sock_filter filter[] = {
		BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
			 (offsetof(struct seccomp_data, arch))),
		BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SEITAN_AUDIT_ARCH, 0, 3),
		BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
			 (offsetof(struct seccomp_data, nr))),
		BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1),
		BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF),
		BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
	};
	return install_filter(
		filter, (unsigned short)(sizeof(filter) / sizeof(filter[0])));

}
int install_notification_filter(struct args_target *at)
{
	return install_single_syscall(at->nr);
}

int target()
{
	int buf = 0;
	if (at->install_filter(at) < 0) {
		return -1;
	}

	at->ret = syscall(at->nr, at->targs[0], at->targs[1], at->targs[2],
			  at->targs[3], at->targs[4], at->targs[5]);
	at->err = errno;
        if (at->open_path) {
                if ((at->fd = open(path, O_CREAT | O_RDONLY)) < 0) {
                        perror("open");
                        return -1;
                }
        }
        if (at->check_fd)
                read(pipefd[0], &buf, 1);

        close(pipefd[0]);

        write(pipefd[1], &buf, 1);
        close(pipefd[1]);
        exit(0);
}

pid_t do_clone(int (*fn)(void *), void *arg, int flags)
{
	char stack[STACK_SIZE];
	pid_t child;

	child = clone(fn, stack + sizeof(stack), flags, arg);
	if (child == -1) {
		perror("clone");
		return -1;
	}
	return child;
}

int create_test_fd()
{
	return open("/tmp", O_RDWR | O_TMPFILE);
}

int get_fd_notifier(pid_t pid)
{
	char path[PATH_MAX + 1];
	int pidfd, notifier;
	int fd = -1;

	snprintf(path, sizeof(path), "/proc/%d/fd", pid);
	while (fd < 0) {
		sleep(2);
		fd = find_fd_seccomp_notifier(path);
	}
	ck_assert_int_ge(fd, 0);
	pidfd = syscall(SYS_pidfd_open, pid, 0);
	ck_assert_msg(pidfd >= 0, strerror(errno));
	sleep(1);
	notifier = syscall(SYS_pidfd_getfd, pidfd, fd, 0);
	ck_assert_msg(notifier >= 0, strerror(errno));
	return notifier;
}

void target_exit()
{
	int status;

	waitpid(-1, &status, WUNTRACED | WNOHANG);
	if (WEXITSTATUS(status) != 0) {
		fprintf(stderr, "target process exited with an error\n");
		exit(-1);
	}
}

void check_target_fd(int pid, int fd)
{
	int buf = 0;

	ck_assert(has_fd(pid, fd));
	write(pipefd[1], &buf, 1);
	close(pipefd[1]);
}

bool has_fd(int pid, int fd)
{
	char path[PATH_MAX + 1];

	snprintf(path, sizeof(path), "/proc/%d/fd/%d", pid, fd);
	return access(path, F_OK) == 0;
}

void check_target_result(long ret, int err, bool ignore_ret)
{
	int buf;

	read(pipefd[0], &buf, 1);
	if (!ignore_ret)
		ck_assert_msg(at->ret == ret,
			      "expect return value %ld to be equal to %ld",
			      at->ret, ret);
	ck_assert_int_eq(at->err, err);
	ck_assert_int_eq(close(pipefd[0]), 0);
}

void check_target_result_nonegative()
{
	int buf;

	read(pipefd[0], &buf, 1);
	ck_assert_msg(at->ret > -1,
		      "expect return value %ld to be greater then -1", at->ret);
	ck_assert_int_eq(at->err, 0);
	ck_assert_int_eq(close(pipefd[0]), 0);
}

void continue_target()
{
	struct seccomp_notif_resp resp;
	int ret;

	ret = ioctl(notifyfd, SECCOMP_IOCTL_NOTIF_ID_VALID, &req.id);
	ck_assert_msg(ret == 0, strerror(errno));
	resp.id = req.id;
	resp.flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE;
	resp.error = 0;
	resp.val = 0;
	ret = ioctl(notifyfd, SECCOMP_IOCTL_NOTIF_SEND, &resp);
	ck_assert_msg(ret == 0, strerror(errno));
}

void mock_syscall_target()
{
	struct seccomp_notif_resp resp;
	int ret;

	ret = ioctl(notifyfd, SECCOMP_IOCTL_NOTIF_ID_VALID, &req.id);
	ck_assert_msg(ret == 0, strerror(errno));
	resp.id = req.id;
	resp.flags = 0;
	resp.error = 0;
	resp.val = 0;
	ret = ioctl(notifyfd, SECCOMP_IOCTL_NOTIF_SEND, &resp);
	ck_assert_msg(ret == 0, strerror(errno));
}

void set_args_no_check(struct args_target *at)
{
	for (unsigned int i = 0; i < 6; i++)
		at->args[i].cmp = NO_CHECK;
}

static int set_ns_flags(bool ns[], int flags)
{
	unsigned int i;

	for (i = 0; i < NS_NUM; i++) {
		if (!ns[i] || i == NS_NONE)
			continue;
		switch (i) {
		case NS_CGROUP:
			flags |= CLONE_NEWCGROUP;
			break;
		case NS_IPC:
			flags |= CLONE_NEWIPC;
			break;
		case NS_NET:
			flags |= CLONE_NEWNET;
			break;
		case NS_MOUNT:
			flags |= CLONE_NEWNS;
			break;
		case NS_PID:
			flags |= CLONE_NEWPID;
			break;
		case NS_USER:
			flags |= CLONE_NEWUSER;
			break;
		case NS_UTS:
			flags |= CLONE_NEWUTS;
			break;
		case NS_TIME:
			fprintf(stderr,
				"option NS_TIME not suppoted by clone\n");
			break;
		default:
			fprintf(stderr, "unrecognized option %d\n", i);
		}
	}
	return flags;
}

void setup()
{
	int ret;

	signal(SIGCHLD, target_exit);
	ck_assert_int_ne(pipe(pipefd), -1);
	if (at->target == NULL)
		at->target = target;
	pid = do_clone(at->target, at->tclone, set_ns_flags(at->ns, SIGCHLD));
	ck_assert_int_ge(pid, 0);

	/* Use write pipe to sync the target for checking the existance of the fd */
	if (!at->check_fd)
		ck_assert_int_ne(close(pipefd[1]), -1);

	notifyfd = get_fd_notifier(pid);

	memset(&req, 0, sizeof(req));
	ret = ioctl(notifyfd, SECCOMP_IOCTL_NOTIF_RECV, &req);
	ck_assert_msg(ret == 0, strerror(errno));
	ck_assert_msg((req.data).nr == at->nr, "filter syscall nr: %d",
		      (req.data).nr);
}

void teardown()
{
	if (at != NULL)
		munmap(at, sizeof(struct args_target));
	unlink(path);
}

void ck_stderr()
{
	setbuf(stderr, stderr_buff);
}

void ck_stdout()
{
	setbuf(stdout, stdout_buff);
}

void ck_error_msg(char *s)
{
	ck_assert_msg(strstr(stderr_buff, s) != NULL, "err=\"%s\" doesn't contain \"%s\" ",
		      stderr_buff, s);
}