aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2016-05-26 14:47:01 -0400
committerKees Cook <keescook@chromium.org>2016-06-14 13:54:38 -0400
commit58d0a862f573c3354fa912603ef5a4db188774e7 (patch)
tree7d7f4c5ad0c47c9353da6a4528aeaab1f4d2088d
parent40d273782ff16fe1a7445cc05c66a447dfea3433 (diff)
seccomp: add tests for ptrace hole
One problem with seccomp was that ptrace could be used to change a syscall after seccomp filtering had completed. This was a well documented limitation, and it was recommended to block ptrace when defining a filter to avoid this problem. This can be quite a limitation for containers or other places where ptrace is desired even under seccomp filters. This adds tests for both SECCOMP_RET_TRACE and PTRACE_SYSCALL manipulations. Signed-off-by: Kees Cook <keescook@chromium.org> Cc: Andy Lutomirski <luto@kernel.org>
-rw-r--r--tools/testing/selftests/seccomp/seccomp_bpf.c176
1 files changed, 165 insertions, 11 deletions
diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index 2e58549b2f02..03f1fa495d74 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -1021,8 +1021,8 @@ void tracer_stop(int sig)
1021typedef void tracer_func_t(struct __test_metadata *_metadata, 1021typedef void tracer_func_t(struct __test_metadata *_metadata,
1022 pid_t tracee, int status, void *args); 1022 pid_t tracee, int status, void *args);
1023 1023
1024void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee, 1024void start_tracer(struct __test_metadata *_metadata, int fd, pid_t tracee,
1025 tracer_func_t tracer_func, void *args) 1025 tracer_func_t tracer_func, void *args, bool ptrace_syscall)
1026{ 1026{
1027 int ret = -1; 1027 int ret = -1;
1028 struct sigaction action = { 1028 struct sigaction action = {
@@ -1042,12 +1042,16 @@ void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee,
1042 /* Wait for attach stop */ 1042 /* Wait for attach stop */
1043 wait(NULL); 1043 wait(NULL);
1044 1044
1045 ret = ptrace(PTRACE_SETOPTIONS, tracee, NULL, PTRACE_O_TRACESECCOMP); 1045 ret = ptrace(PTRACE_SETOPTIONS, tracee, NULL, ptrace_syscall ?
1046 PTRACE_O_TRACESYSGOOD :
1047 PTRACE_O_TRACESECCOMP);
1046 ASSERT_EQ(0, ret) { 1048 ASSERT_EQ(0, ret) {
1047 TH_LOG("Failed to set PTRACE_O_TRACESECCOMP"); 1049 TH_LOG("Failed to set PTRACE_O_TRACESECCOMP");
1048 kill(tracee, SIGKILL); 1050 kill(tracee, SIGKILL);
1049 } 1051 }
1050 ptrace(PTRACE_CONT, tracee, NULL, 0); 1052 ret = ptrace(ptrace_syscall ? PTRACE_SYSCALL : PTRACE_CONT,
1053 tracee, NULL, 0);
1054 ASSERT_EQ(0, ret);
1051 1055
1052 /* Unblock the tracee */ 1056 /* Unblock the tracee */
1053 ASSERT_EQ(1, write(fd, "A", 1)); 1057 ASSERT_EQ(1, write(fd, "A", 1));
@@ -1063,12 +1067,13 @@ void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee,
1063 /* Child is dead. Time to go. */ 1067 /* Child is dead. Time to go. */
1064 return; 1068 return;
1065 1069
1066 /* Make sure this is a seccomp event. */ 1070 /* Check if this is a seccomp event. */
1067 ASSERT_EQ(true, IS_SECCOMP_EVENT(status)); 1071 ASSERT_EQ(!ptrace_syscall, IS_SECCOMP_EVENT(status));
1068 1072
1069 tracer_func(_metadata, tracee, status, args); 1073 tracer_func(_metadata, tracee, status, args);
1070 1074
1071 ret = ptrace(PTRACE_CONT, tracee, NULL, NULL); 1075 ret = ptrace(ptrace_syscall ? PTRACE_SYSCALL : PTRACE_CONT,
1076 tracee, NULL, 0);
1072 ASSERT_EQ(0, ret); 1077 ASSERT_EQ(0, ret);
1073 } 1078 }
1074 /* Directly report the status of our test harness results. */ 1079 /* Directly report the status of our test harness results. */
@@ -1079,7 +1084,7 @@ void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee,
1079void cont_handler(int num) 1084void cont_handler(int num)
1080{ } 1085{ }
1081pid_t setup_trace_fixture(struct __test_metadata *_metadata, 1086pid_t setup_trace_fixture(struct __test_metadata *_metadata,
1082 tracer_func_t func, void *args) 1087 tracer_func_t func, void *args, bool ptrace_syscall)
1083{ 1088{
1084 char sync; 1089 char sync;
1085 int pipefd[2]; 1090 int pipefd[2];
@@ -1095,7 +1100,8 @@ pid_t setup_trace_fixture(struct __test_metadata *_metadata,
1095 signal(SIGALRM, cont_handler); 1100 signal(SIGALRM, cont_handler);
1096 if (tracer_pid == 0) { 1101 if (tracer_pid == 0) {
1097 close(pipefd[0]); 1102 close(pipefd[0]);
1098 tracer(_metadata, pipefd[1], tracee, func, args); 1103 start_tracer(_metadata, pipefd[1], tracee, func, args,
1104 ptrace_syscall);
1099 syscall(__NR_exit, 0); 1105 syscall(__NR_exit, 0);
1100 } 1106 }
1101 close(pipefd[1]); 1107 close(pipefd[1]);
@@ -1177,7 +1183,7 @@ FIXTURE_SETUP(TRACE_poke)
1177 1183
1178 /* Launch tracer. */ 1184 /* Launch tracer. */
1179 self->tracer = setup_trace_fixture(_metadata, tracer_poke, 1185 self->tracer = setup_trace_fixture(_metadata, tracer_poke,
1180 &self->tracer_args); 1186 &self->tracer_args, false);
1181} 1187}
1182 1188
1183FIXTURE_TEARDOWN(TRACE_poke) 1189FIXTURE_TEARDOWN(TRACE_poke)
@@ -1399,6 +1405,29 @@ void tracer_syscall(struct __test_metadata *_metadata, pid_t tracee,
1399 1405
1400} 1406}
1401 1407
1408void tracer_ptrace(struct __test_metadata *_metadata, pid_t tracee,
1409 int status, void *args)
1410{
1411 int ret, nr;
1412 unsigned long msg;
1413 static bool entry;
1414
1415 /* Make sure we got an empty message. */
1416 ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg);
1417 EXPECT_EQ(0, ret);
1418 EXPECT_EQ(0, msg);
1419
1420 /* The only way to tell PTRACE_SYSCALL entry/exit is by counting. */
1421 entry = !entry;
1422 if (!entry)
1423 return;
1424
1425 nr = get_syscall(_metadata, tracee);
1426
1427 if (nr == __NR_getpid)
1428 change_syscall(_metadata, tracee, __NR_getppid);
1429}
1430
1402FIXTURE_DATA(TRACE_syscall) { 1431FIXTURE_DATA(TRACE_syscall) {
1403 struct sock_fprog prog; 1432 struct sock_fprog prog;
1404 pid_t tracer, mytid, mypid, parent; 1433 pid_t tracer, mytid, mypid, parent;
@@ -1440,7 +1469,8 @@ FIXTURE_SETUP(TRACE_syscall)
1440 ASSERT_NE(self->parent, self->mypid); 1469 ASSERT_NE(self->parent, self->mypid);
1441 1470
1442 /* Launch tracer. */ 1471 /* Launch tracer. */
1443 self->tracer = setup_trace_fixture(_metadata, tracer_syscall, NULL); 1472 self->tracer = setup_trace_fixture(_metadata, tracer_syscall, NULL,
1473 false);
1444} 1474}
1445 1475
1446FIXTURE_TEARDOWN(TRACE_syscall) 1476FIXTURE_TEARDOWN(TRACE_syscall)
@@ -1500,6 +1530,130 @@ TEST_F(TRACE_syscall, syscall_dropped)
1500 EXPECT_NE(self->mytid, syscall(__NR_gettid)); 1530 EXPECT_NE(self->mytid, syscall(__NR_gettid));
1501} 1531}
1502 1532
1533TEST_F(TRACE_syscall, skip_after_RET_TRACE)
1534{
1535 struct sock_filter filter[] = {
1536 BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
1537 offsetof(struct seccomp_data, nr)),
1538 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
1539 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | EPERM),
1540 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
1541 };
1542 struct sock_fprog prog = {
1543 .len = (unsigned short)ARRAY_SIZE(filter),
1544 .filter = filter,
1545 };
1546 long ret;
1547
1548 ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
1549 ASSERT_EQ(0, ret);
1550
1551 /* Install fixture filter. */
1552 ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
1553 ASSERT_EQ(0, ret);
1554
1555 /* Install "errno on getppid" filter. */
1556 ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
1557 ASSERT_EQ(0, ret);
1558
1559 /* Tracer will redirect getpid to getppid, and we should see EPERM. */
1560 EXPECT_EQ(-1, syscall(__NR_getpid));
1561 EXPECT_EQ(EPERM, errno);
1562}
1563
1564TEST_F_SIGNAL(TRACE_syscall, kill_after_RET_TRACE, SIGSYS)
1565{
1566 struct sock_filter filter[] = {
1567 BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
1568 offsetof(struct seccomp_data, nr)),
1569 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
1570 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
1571 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
1572 };
1573 struct sock_fprog prog = {
1574 .len = (unsigned short)ARRAY_SIZE(filter),
1575 .filter = filter,
1576 };
1577 long ret;
1578
1579 ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
1580 ASSERT_EQ(0, ret);
1581
1582 /* Install fixture filter. */
1583 ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
1584 ASSERT_EQ(0, ret);
1585
1586 /* Install "death on getppid" filter. */
1587 ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
1588 ASSERT_EQ(0, ret);
1589
1590 /* Tracer will redirect getpid to getppid, and we should die. */
1591 EXPECT_NE(self->mypid, syscall(__NR_getpid));
1592}
1593
1594TEST_F(TRACE_syscall, skip_after_ptrace)
1595{
1596 struct sock_filter filter[] = {
1597 BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
1598 offsetof(struct seccomp_data, nr)),
1599 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
1600 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | EPERM),
1601 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
1602 };
1603 struct sock_fprog prog = {
1604 .len = (unsigned short)ARRAY_SIZE(filter),
1605 .filter = filter,
1606 };
1607 long ret;
1608
1609 /* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
1610 teardown_trace_fixture(_metadata, self->tracer);
1611 self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
1612 true);
1613
1614 ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
1615 ASSERT_EQ(0, ret);
1616
1617 /* Install "errno on getppid" filter. */
1618 ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
1619 ASSERT_EQ(0, ret);
1620
1621 /* Tracer will redirect getpid to getppid, and we should see EPERM. */
1622 EXPECT_EQ(-1, syscall(__NR_getpid));
1623 EXPECT_EQ(EPERM, errno);
1624}
1625
1626TEST_F_SIGNAL(TRACE_syscall, kill_after_ptrace, SIGSYS)
1627{
1628 struct sock_filter filter[] = {
1629 BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
1630 offsetof(struct seccomp_data, nr)),
1631 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
1632 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
1633 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
1634 };
1635 struct sock_fprog prog = {
1636 .len = (unsigned short)ARRAY_SIZE(filter),
1637 .filter = filter,
1638 };
1639 long ret;
1640
1641 /* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
1642 teardown_trace_fixture(_metadata, self->tracer);
1643 self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
1644 true);
1645
1646 ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
1647 ASSERT_EQ(0, ret);
1648
1649 /* Install "death on getppid" filter. */
1650 ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
1651 ASSERT_EQ(0, ret);
1652
1653 /* Tracer will redirect getpid to getppid, and we should die. */
1654 EXPECT_NE(self->mypid, syscall(__NR_getpid));
1655}
1656
1503#ifndef __NR_seccomp 1657#ifndef __NR_seccomp
1504# if defined(__i386__) 1658# if defined(__i386__)
1505# define __NR_seccomp 354 1659# define __NR_seccomp 354