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) { pthread_t th; void *p = NULL; if (pthread_mutex_init(&getpid_wait, NULL)) { pr_perror("pthread_mutex_init"); _exit(1); } sk = sk_pair[1]; close(sk_pair[0]); if (filter_syscall(__NR_getpid, 0) < 0) _exit(1); zdtm_seccomp = 1; pthread_mutex_lock(&getpid_wait); pthread_create(&th, NULL, wait_and_getpid, NULL); 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); } /* Now we have c/r'd with a shared filter, so let's install * another filter with TSYNC and make sure that it is * inherited. */ if (filter_syscall(__NR_ptrace, SECCOMP_FILTER_FLAG_TSYNC) < 0) _exit(1); pthread_mutex_unlock(&getpid_wait); if (pthread_join(th, &p) != 0) { pr_perror("pthread_join"); exit(1); } /* Here we're abusing pthread exit slightly: if the thread gets * to call pthread_exit, the value of p is one, but if it gets * killed pthread_join doesn't set a value since the thread * didn't, so the value is null; we exit 0 to indicate success * as usual. */ syscall(__NR_exit, p); } 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 (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { pr_perror("expected 0 exit, got %d\n", WEXITSTATUS(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; }
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_perror("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) { pid_t pid2; sk = sk_pair[1]; close(sk_pair[0]); if (filter_syscall(__NR_ptrace) < 0) _exit(1); if (filter_syscall(__NR_fstat) < 0) _exit(1); zdtm_seccomp = 1; test_msg("SECCOMP_MODE_FILTER is enabled\n"); pid2 = fork(); if (pid2 < 0) _exit(1); if (!pid2) { 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); _exit(1); } if (waitpid(pid2, &status, 0) != pid2) { pr_perror("waitpid"); _exit(1); } if (WTERMSIG(status) != SIGSYS) { pr_perror("expected SIGSYS, got %d\n", WTERMSIG(status)); _exit(1); } _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 (mode != SECCOMP_MODE_FILTER) { fail("seccomp mode mismatch %d\n", mode); return 1; } if (waitpid(pid, &status, 0) != pid) { pr_perror("waitpid"); _exit(1); } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { fail("bad exit status"); return 1; } pass(); return 0; err: kill(pid, SIGKILL); return 1; }