diff options
author | Kees Cook <keescook@chromium.org> | 2016-05-26 14:47:01 -0400 |
---|---|---|
committer | Kees Cook <keescook@chromium.org> | 2016-06-14 13:54:38 -0400 |
commit | 58d0a862f573c3354fa912603ef5a4db188774e7 (patch) | |
tree | 7d7f4c5ad0c47c9353da6a4528aeaab1f4d2088d | |
parent | 40d273782ff16fe1a7445cc05c66a447dfea3433 (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.c | 176 |
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) | |||
1021 | typedef void tracer_func_t(struct __test_metadata *_metadata, | 1021 | typedef 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 | ||
1024 | void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee, | 1024 | void 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, | |||
1079 | void cont_handler(int num) | 1084 | void cont_handler(int num) |
1080 | { } | 1085 | { } |
1081 | pid_t setup_trace_fixture(struct __test_metadata *_metadata, | 1086 | pid_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 | ||
1183 | FIXTURE_TEARDOWN(TRACE_poke) | 1189 | FIXTURE_TEARDOWN(TRACE_poke) |
@@ -1399,6 +1405,29 @@ void tracer_syscall(struct __test_metadata *_metadata, pid_t tracee, | |||
1399 | 1405 | ||
1400 | } | 1406 | } |
1401 | 1407 | ||
1408 | void 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 | |||
1402 | FIXTURE_DATA(TRACE_syscall) { | 1431 | FIXTURE_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 | ||
1446 | FIXTURE_TEARDOWN(TRACE_syscall) | 1476 | FIXTURE_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 | ||
1533 | TEST_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 | |||
1564 | TEST_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 | |||
1594 | TEST_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 | |||
1626 | TEST_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 |