Skip to content

Commit f3e1821

Browse files
committed
selftests/seccomp: Test thread vs process killing
This verifies that SECCOMP_RET_KILL_PROCESS is higher priority than SECCOMP_RET_KILL_THREAD. (This also moves a bunch of defines up earlier in the file to use them earlier.) Signed-off-by: Kees Cook <[email protected]> Reviewed-by: Tyler Hicks <[email protected]>
1 parent 0466bdb commit f3e1821

File tree

1 file changed

+168
-60
lines changed

1 file changed

+168
-60
lines changed

tools/testing/selftests/seccomp/seccomp_bpf.c

Lines changed: 168 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,17 @@
6868
#define SECCOMP_MODE_FILTER 2
6969
#endif
7070

71-
#ifndef SECCOMP_RET_KILL_THREAD
71+
#ifndef SECCOMP_RET_ALLOW
72+
struct seccomp_data {
73+
int nr;
74+
__u32 arch;
75+
__u64 instruction_pointer;
76+
__u64 args[6];
77+
};
78+
#endif
79+
80+
#ifndef SECCOMP_RET_KILL_PROCESS
81+
#define SECCOMP_RET_KILL_PROCESS 0x80000000U /* kill the process */
7282
#define SECCOMP_RET_KILL_THREAD 0x00000000U /* kill the thread */
7383
#endif
7484
#ifndef SECCOMP_RET_KILL
@@ -82,17 +92,53 @@
8292
#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */
8393
#endif
8494

85-
#ifndef SECCOMP_RET_ACTION
86-
/* Masks for the return value sections. */
87-
#define SECCOMP_RET_ACTION 0x7fff0000U
88-
#define SECCOMP_RET_DATA 0x0000ffffU
95+
#ifndef __NR_seccomp
96+
# if defined(__i386__)
97+
# define __NR_seccomp 354
98+
# elif defined(__x86_64__)
99+
# define __NR_seccomp 317
100+
# elif defined(__arm__)
101+
# define __NR_seccomp 383
102+
# elif defined(__aarch64__)
103+
# define __NR_seccomp 277
104+
# elif defined(__hppa__)
105+
# define __NR_seccomp 338
106+
# elif defined(__powerpc__)
107+
# define __NR_seccomp 358
108+
# elif defined(__s390__)
109+
# define __NR_seccomp 348
110+
# else
111+
# warning "seccomp syscall number unknown for this architecture"
112+
# define __NR_seccomp 0xffff
113+
# endif
114+
#endif
89115

90-
struct seccomp_data {
91-
int nr;
92-
__u32 arch;
93-
__u64 instruction_pointer;
94-
__u64 args[6];
95-
};
116+
#ifndef SECCOMP_SET_MODE_STRICT
117+
#define SECCOMP_SET_MODE_STRICT 0
118+
#endif
119+
120+
#ifndef SECCOMP_SET_MODE_FILTER
121+
#define SECCOMP_SET_MODE_FILTER 1
122+
#endif
123+
124+
#ifndef SECCOMP_GET_ACTION_AVAIL
125+
#define SECCOMP_GET_ACTION_AVAIL 2
126+
#endif
127+
128+
#ifndef SECCOMP_FILTER_FLAG_TSYNC
129+
#define SECCOMP_FILTER_FLAG_TSYNC 1
130+
#endif
131+
132+
#ifndef SECCOMP_FILTER_FLAG_LOG
133+
#define SECCOMP_FILTER_FLAG_LOG 2
134+
#endif
135+
136+
#ifndef seccomp
137+
int seccomp(unsigned int op, unsigned int flags, void *args)
138+
{
139+
errno = 0;
140+
return syscall(__NR_seccomp, op, flags, args);
141+
}
96142
#endif
97143

98144
#if __BYTE_ORDER == __LITTLE_ENDIAN
@@ -550,6 +596,117 @@ TEST_SIGNAL(KILL_one_arg_six, SIGSYS)
550596
close(fd);
551597
}
552598

599+
/* This is a thread task to die via seccomp filter violation. */
600+
void *kill_thread(void *data)
601+
{
602+
bool die = (bool)data;
603+
604+
if (die) {
605+
prctl(PR_GET_SECCOMP, 0, 0, 0, 0);
606+
return (void *)SIBLING_EXIT_FAILURE;
607+
}
608+
609+
return (void *)SIBLING_EXIT_UNKILLED;
610+
}
611+
612+
/* Prepare a thread that will kill itself or both of us. */
613+
void kill_thread_or_group(struct __test_metadata *_metadata, bool kill_process)
614+
{
615+
pthread_t thread;
616+
void *status;
617+
/* Kill only when calling __NR_prctl. */
618+
struct sock_filter filter_thread[] = {
619+
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
620+
offsetof(struct seccomp_data, nr)),
621+
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_prctl, 0, 1),
622+
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL_THREAD),
623+
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
624+
};
625+
struct sock_fprog prog_thread = {
626+
.len = (unsigned short)ARRAY_SIZE(filter_thread),
627+
.filter = filter_thread,
628+
};
629+
struct sock_filter filter_process[] = {
630+
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
631+
offsetof(struct seccomp_data, nr)),
632+
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_prctl, 0, 1),
633+
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL_PROCESS),
634+
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
635+
};
636+
struct sock_fprog prog_process = {
637+
.len = (unsigned short)ARRAY_SIZE(filter_process),
638+
.filter = filter_process,
639+
};
640+
641+
ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
642+
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
643+
}
644+
645+
ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0,
646+
kill_process ? &prog_process : &prog_thread));
647+
648+
/*
649+
* Add the KILL_THREAD rule again to make sure that the KILL_PROCESS
650+
* flag cannot be downgraded by a new filter.
651+
*/
652+
ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog_thread));
653+
654+
/* Start a thread that will exit immediately. */
655+
ASSERT_EQ(0, pthread_create(&thread, NULL, kill_thread, (void *)false));
656+
ASSERT_EQ(0, pthread_join(thread, &status));
657+
ASSERT_EQ(SIBLING_EXIT_UNKILLED, (unsigned long)status);
658+
659+
/* Start a thread that will die immediately. */
660+
ASSERT_EQ(0, pthread_create(&thread, NULL, kill_thread, (void *)true));
661+
ASSERT_EQ(0, pthread_join(thread, &status));
662+
ASSERT_NE(SIBLING_EXIT_FAILURE, (unsigned long)status);
663+
664+
/*
665+
* If we get here, only the spawned thread died. Let the parent know
666+
* the whole process didn't die (i.e. this thread, the spawner,
667+
* stayed running).
668+
*/
669+
exit(42);
670+
}
671+
672+
TEST(KILL_thread)
673+
{
674+
int status;
675+
pid_t child_pid;
676+
677+
child_pid = fork();
678+
ASSERT_LE(0, child_pid);
679+
if (child_pid == 0) {
680+
kill_thread_or_group(_metadata, false);
681+
_exit(38);
682+
}
683+
684+
ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
685+
686+
/* If only the thread was killed, we'll see exit 42. */
687+
ASSERT_TRUE(WIFEXITED(status));
688+
ASSERT_EQ(42, WEXITSTATUS(status));
689+
}
690+
691+
TEST(KILL_process)
692+
{
693+
int status;
694+
pid_t child_pid;
695+
696+
child_pid = fork();
697+
ASSERT_LE(0, child_pid);
698+
if (child_pid == 0) {
699+
kill_thread_or_group(_metadata, true);
700+
_exit(38);
701+
}
702+
703+
ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
704+
705+
/* If the entire process was killed, we'll see SIGSYS. */
706+
ASSERT_TRUE(WIFSIGNALED(status));
707+
ASSERT_EQ(SIGSYS, WTERMSIG(status));
708+
}
709+
553710
/* TODO(wad) add 64-bit versus 32-bit arg tests. */
554711
TEST(arg_out_of_range)
555712
{
@@ -1800,55 +1957,6 @@ TEST_F_SIGNAL(TRACE_syscall, kill_after_ptrace, SIGSYS)
18001957
EXPECT_NE(self->mypid, syscall(__NR_getpid));
18011958
}
18021959

1803-
#ifndef __NR_seccomp
1804-
# if defined(__i386__)
1805-
# define __NR_seccomp 354
1806-
# elif defined(__x86_64__)
1807-
# define __NR_seccomp 317
1808-
# elif defined(__arm__)
1809-
# define __NR_seccomp 383
1810-
# elif defined(__aarch64__)
1811-
# define __NR_seccomp 277
1812-
# elif defined(__hppa__)
1813-
# define __NR_seccomp 338
1814-
# elif defined(__powerpc__)
1815-
# define __NR_seccomp 358
1816-
# elif defined(__s390__)
1817-
# define __NR_seccomp 348
1818-
# else
1819-
# warning "seccomp syscall number unknown for this architecture"
1820-
# define __NR_seccomp 0xffff
1821-
# endif
1822-
#endif
1823-
1824-
#ifndef SECCOMP_SET_MODE_STRICT
1825-
#define SECCOMP_SET_MODE_STRICT 0
1826-
#endif
1827-
1828-
#ifndef SECCOMP_SET_MODE_FILTER
1829-
#define SECCOMP_SET_MODE_FILTER 1
1830-
#endif
1831-
1832-
#ifndef SECCOMP_GET_ACTION_AVAIL
1833-
#define SECCOMP_GET_ACTION_AVAIL 2
1834-
#endif
1835-
1836-
#ifndef SECCOMP_FILTER_FLAG_TSYNC
1837-
#define SECCOMP_FILTER_FLAG_TSYNC 1
1838-
#endif
1839-
1840-
#ifndef SECCOMP_FILTER_FLAG_LOG
1841-
#define SECCOMP_FILTER_FLAG_LOG 2
1842-
#endif
1843-
1844-
#ifndef seccomp
1845-
int seccomp(unsigned int op, unsigned int flags, void *args)
1846-
{
1847-
errno = 0;
1848-
return syscall(__NR_seccomp, op, flags, args);
1849-
}
1850-
#endif
1851-
18521960
TEST(seccomp_syscall)
18531961
{
18541962
struct sock_filter filter[] = {

0 commit comments

Comments
 (0)