diff --git a/test/zdtm.sh b/test/zdtm.sh index 4fcb1fa8ed..b5b0a5f77a 100755 --- a/test/zdtm.sh +++ b/test/zdtm.sh @@ -245,6 +245,9 @@ generate_test_list() static/seccomp_strict " + TEST_SECCOMP_FILTERS=" + static/seccomp_filter + " $CRIU check -v0 --feature "mnt_id" if [ $? -eq 0 ]; then @@ -273,6 +276,11 @@ generate_test_list() TEST_LIST="$TEST_LIST$TEST_SECCOMP_SUSPEND" fi + $CRIU check -v0 --feature "seccomp_filters" + if [ $? -eq 0 ]; then + TEST_LIST="$TEST_LIST$TEST_SECCOMP_FILTERS" + fi + # ns/static/clean_mntns: proc can't be mounted in userns, if it isn't mounted yet BLACKLIST_FOR_USERNS=" @@ -359,6 +367,7 @@ sockets00 cow01 apparmor seccomp_strict +seccomp_filter different_creds inotify01 ipc_namespace diff --git a/test/zdtm/.gitignore b/test/zdtm/.gitignore index 2b7010229e..6906618807 100644 --- a/test/zdtm/.gitignore +++ b/test/zdtm/.gitignore @@ -111,6 +111,7 @@ /live/static/rtc /live/static/sched_policy00 /live/static/sched_prio00 +/live/static/seccomp_filter /live/static/seccomp_strict /live/static/selfexe00 /live/static/sem diff --git a/test/zdtm/live/static/Makefile b/test/zdtm/live/static/Makefile index 116daeda3b..70626e8e76 100644 --- a/test/zdtm/live/static/Makefile +++ b/test/zdtm/live/static/Makefile @@ -128,6 +128,7 @@ TST_NOFILE = \ fd \ apparmor \ seccomp_strict \ + seccomp_filter \ different_creds \ vsx \ bridge \ diff --git a/test/zdtm/live/static/seccomp_filter.c b/test/zdtm/live/static/seccomp_filter.c new file mode 100644 index 0000000000..20f5722e42 --- /dev/null +++ b/test/zdtm/live/static/seccomp_filter.c @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "zdtmtst.h" + +const char *test_doc = "Check that SECCOMP_MODE_FILTER is restored"; +const char *test_author = "Tycho Andersen "; + +int get_seccomp_mode(pid_t pid) +{ + FILE *f; + char buf[PATH_MAX]; + + sprintf(buf, "/proc/%d/status", pid); + f = fopen(buf, "r+"); + if (!f) { + pr_perror("fopen failed"); + return -1; + } + + while (NULL != fgets(buf, sizeof(buf), f)) { + int mode; + + if (sscanf(buf, "Seccomp:\t%d", &mode) != 1) + continue; + + fclose(f); + return mode; + } + fclose(f); + + return -1; +} + +int filter_syscall(int syscall_nr) +{ + struct sock_filter filter[] = { + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL), + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), + }; + + struct sock_fprog bpf_prog = { + .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), + .filter = filter, + }; + + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bpf_prog) < 0) { + pr_err("prctl failed"); + return -1; + } + + return 0; +} + +int main(int argc, char ** argv) +{ + pid_t pid; + int mode, status; + int sk_pair[2], sk, ret; + char c = 'K'; + + test_init(argc, argv); + + if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair)) { + pr_perror("socketpair"); + return -1; + } + + pid = fork(); + if (pid < 0) { + pr_perror("fork"); + return -1; + } + + if (pid == 0) { + + sk = sk_pair[1]; + close(sk_pair[0]); + + /* + * Let's install a few filters separately to make sure the + * chaining actually works. + */ + if (filter_syscall(__NR_ptrace) < 0) + _exit(1); + + if (filter_syscall(__NR_getpid) < 0) + _exit(1); + + /* FIXME: seccomp requires a task to be root in its user ns in + * order to install filters for security reasons, so that + * unprivileged parents cannot take over privileged childen. + * However, we restore euids before we restore seccomp filters, + * so if someone does a setuid(1000) here, the restore will + * fail. We need to reorder some things so that the other creds + * restore takes place after seccomp state is set; except that + * the tasks need to be ptraced so the seccomp filters + * potentially don't kill the task for calling setuid(). + */ + + zdtm_seccomp = 1; + test_msg("SECCOMP_MODE_FILTER is enabled\n"); + + if (write(sk, &c, 1) != 1) { + pr_perror("write"); + _exit(1); + } + + if (read(sk, &c, 1) != 1) { + pr_perror("read"); + _exit(1); + } + + /* We expect to be killed by our policy above. */ + ptrace(PTRACE_TRACEME); + + syscall(__NR_exit, 0); + } + + sk = sk_pair[0]; + close(sk_pair[1]); + + if ((ret = read(sk, &c, 1)) != 1) { + pr_perror("read %d", ret); + goto err; + } + + test_daemon(); + test_waitsig(); + + mode = get_seccomp_mode(pid); + if (write(sk, &c, 1) != 1) { + pr_perror("write"); + goto err; + } + if (waitpid(pid, &status, 0) != pid) { + pr_perror("waitpid"); + exit(1); + } + + if (WTERMSIG(status) != SIGSYS) { + pr_perror("expected SIGSYS, got %d\n", WTERMSIG(status)); + exit(1); + } + + if (mode != SECCOMP_MODE_FILTER) { + fail("seccomp mode mismatch %d\n", mode); + return 1; + } + + pass(); + + return 0; +err: + kill(pid, SIGKILL); + return 1; +} diff --git a/test/zdtm/live/static/seccomp_filter.desc b/test/zdtm/live/static/seccomp_filter.desc new file mode 100644 index 0000000000..14dd961849 --- /dev/null +++ b/test/zdtm/live/static/seccomp_filter.desc @@ -0,0 +1 @@ +{'flags': 'suid', 'feature': 'seccomp_filters'}