From 6ba947f36e4d56710d09a59115e919bec2232cc5 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Tue, 12 Apr 2016 00:05:43 +0000 Subject: [PATCH] seccomp: fix decoding of sock_fprog and sock_filter structures Always print struct sock_fprog.len. Fix printing of unfetchable elements in sock_filter array. Fix printing of large sock_filter arrays. * seccomp.c (decode_fprog): Rewrite into decode_seccomp_fprog and print_seccomp_fprog. (print_seccomp_filter): Replace decode_fprog with print_seccomp_fprog. * tests/prctl-seccomp-filter-v.c (main): Update. * tests/seccomp-filter-v.c: New file. * tests/seccomp-filter-v.test: New test. * tests/.gitignore: Add seccomp-filter-v. * tests/Makefile.am (check_PROGRAMS): Likewise. (DECODER_TESTS): Add seccomp-filter-v.test. --- seccomp.c | 53 ++++---- tests/.gitignore | 1 + tests/Makefile.am | 2 + tests/prctl-seccomp-filter-v.c | 7 +- tests/seccomp-filter-v.c | 213 +++++++++++++++++++++++++++++++++ tests/seccomp-filter-v.test | 6 + 6 files changed, 256 insertions(+), 26 deletions(-) create mode 100644 tests/seccomp-filter-v.c create mode 100755 tests/seccomp-filter-v.test diff --git a/seccomp.c b/seccomp.c index e85666d7..e62f99a0 100644 --- a/seccomp.c +++ b/seccomp.c @@ -162,32 +162,39 @@ decode_filter(const struct bpf_filter *filter) #endif static void -decode_fprog(struct tcb *tcp, unsigned short len, unsigned long addr) +decode_seccomp_fprog(struct tcb *tcp, unsigned short len, unsigned long addr) { - if (!len || abbrev(tcp)) { - tprintf("{len = %u, filter = ", len); - printaddr(addr); - tprints("}"); - } else { - unsigned int i = 0; - - tprints("["); - while (i < len && i < BPF_MAXINSNS) { - struct bpf_filter filter; - - if (umove(tcp, addr, &filter) < 0) + struct bpf_filter filter; + const unsigned long start_addr = addr; + unsigned int i = 0; + + for (; addr >= start_addr && i < len; ++i, addr += sizeof(filter)) { + if (i) { + tprints(", "); + if (i >= BPF_MAXINSNS) { + tprints("..."); break; - if (i) - tprints(", "); - decode_filter(&filter); - - addr += sizeof(filter); - ++i; + } } - if (i < len) - tprints("..."); - tprints("]"); + if (umove_or_printaddr(tcp, addr, &filter)) + break; + if (!i) + tprints("["); + decode_filter(&filter); } + if (i) + tprints("]"); +} + +static void +print_seccomp_fprog(struct tcb *tcp, unsigned short len, unsigned long addr) +{ + tprintf("{len=%u, filter=", len); + if (abbrev(tcp) || !len) + printaddr(addr); + else + decode_seccomp_fprog(tcp, len, addr); + tprints("}"); } #include "seccomp_fprog.h" @@ -198,7 +205,7 @@ print_seccomp_filter(struct tcb *tcp, unsigned long addr) struct seccomp_fprog fprog; if (fetch_seccomp_fprog(tcp, addr, &fprog)) - decode_fprog(tcp, fprog.len, fprog.filter); + print_seccomp_fprog(tcp, fprog.len, fprog.filter); } static void diff --git a/tests/.gitignore b/tests/.gitignore index 01b62362..3ed6ceea 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -128,6 +128,7 @@ sched_xetattr sched_xetparam sched_xetscheduler scm_rights +seccomp-filter-v seccomp-strict select sendfile diff --git a/tests/Makefile.am b/tests/Makefile.am index 95720154..db33f4df 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -177,6 +177,7 @@ check_PROGRAMS = \ sched_xetparam \ sched_xetscheduler \ scm_rights \ + seccomp-filter-v \ seccomp-strict \ select \ sendfile \ @@ -375,6 +376,7 @@ DECODER_TESTS = \ sched_xetparam.test \ sched_xetscheduler.test \ scm_rights-fd.test \ + seccomp-filter-v.test \ seccomp-strict.test \ select.test \ sendfile.test \ diff --git a/tests/prctl-seccomp-filter-v.c b/tests/prctl-seccomp-filter-v.c index c7fb137f..a03f202a 100644 --- a/tests/prctl-seccomp-filter-v.c +++ b/tests/prctl-seccomp-filter-v.c @@ -91,7 +91,7 @@ static const struct sock_filter filter[] = { }; static const struct sock_fprog prog = { - .len = sizeof(filter) / sizeof(filter[0]), + .len = ARRAY_SIZE(filter), .filter = (struct sock_filter *) filter, }; @@ -102,7 +102,8 @@ main(void) puts("prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) = 0"); - printf("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, ["); + printf("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, {len=%u, filter=[", + prog.len); printf("BPF_STMT(BPF_LD|BPF_W|BPF_ABS, %#x), ", (unsigned) offsetof(struct seccomp_data, nr)); @@ -116,7 +117,7 @@ main(void) printf("BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL)"); - puts("]) = 0"); + puts("]}) = 0"); puts("+++ exited with 0 +++"); fflush(stdout); diff --git a/tests/seccomp-filter-v.c b/tests/seccomp-filter-v.c new file mode 100644 index 00000000..9a706585 --- /dev/null +++ b/tests/seccomp-filter-v.c @@ -0,0 +1,213 @@ +/* + * Check verbose decoding of seccomp SECCOMP_SET_MODE_FILTER. + * + * Copyright (c) 2015-2016 Dmitry V. Levin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "tests.h" + +#include +#include +#include +#include +#include + +#ifdef HAVE_PRCTL +# include +#endif +#ifdef HAVE_LINUX_SECCOMP_H +# include +#endif +#ifdef HAVE_LINUX_FILTER_H +# include +#endif + +#if defined __NR_seccomp \ + && defined PR_SET_NO_NEW_PRIVS \ + && defined SECCOMP_SET_MODE_FILTER \ + && defined SECCOMP_RET_ERRNO \ + && defined BPF_JUMP \ + && defined BPF_STMT + +#define SOCK_FILTER_ALLOW_SYSCALL(nr) \ + BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, __NR_ ## nr, 0, 1), \ + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW) + +#define SOCK_FILTER_DENY_SYSCALL(nr, err) \ + BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, __NR_ ## nr, 0, 1), \ + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO|(SECCOMP_RET_DATA & (err))) + +#define SOCK_FILTER_KILL_PROCESS \ + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL) + +#define PRINT_ALLOW_SYSCALL(nr) \ + tprintf("BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, %#x, 0, 0x1), " \ + "BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), ", \ + __NR_ ## nr) + +#define PRINT_DENY_SYSCALL(nr, err) \ + tprintf("BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, %#x, 0, 0x1), " \ + "BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO|%#x), ", \ + __NR_ ## nr, err) + +static const struct sock_filter filter_c[] = { + /* load syscall number */ + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, offsetof(struct seccomp_data, nr)), + + /* allow syscalls */ + SOCK_FILTER_ALLOW_SYSCALL(close), + SOCK_FILTER_ALLOW_SYSCALL(exit), + SOCK_FILTER_ALLOW_SYSCALL(exit_group), + + /* deny syscalls */ + SOCK_FILTER_DENY_SYSCALL(sync, EBUSY), + SOCK_FILTER_DENY_SYSCALL(setsid, EPERM), + + /* kill process */ + SOCK_FILTER_KILL_PROCESS +}; + +#ifndef BPF_MAXINSNS +# define BPF_MAXINSNS 4096 +#endif + +int +main(void) +{ + tprintf("%s", ""); + + static const char kill_stmt_txt[] = + "BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL)"; + struct sock_filter *const filter = + tail_memdup(filter_c, sizeof(filter_c)); + struct sock_filter *const big_filter = + tail_alloc(sizeof(*big_filter) * (BPF_MAXINSNS + 1)); + struct sock_fprog *const prog = tail_alloc(sizeof(*prog)); + + int fds[2]; + if (pipe(fds)) + perror_msg_and_fail("pipe"); + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) + perror_msg_and_skip("PR_SET_NO_NEW_PRIVS"); + + prog->filter = filter + ARRAY_SIZE(filter_c); + prog->len = 1; + syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 0, prog); + tprintf("seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=1, filter=%p})" + " = -1 EFAULT (%m)\n", prog->filter); + + prog->filter = filter + ARRAY_SIZE(filter_c) - 1; + prog->len = 3; + syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 0, prog); + tprintf("seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=%u" + ", filter=[%s, %p]}) = -1 EFAULT (%m)\n", + prog->len, kill_stmt_txt, filter + ARRAY_SIZE(filter_c)); + + prog->len = 0; + syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 0, prog); + tprintf("seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=0, filter=%p})" + " = -1 EINVAL (%m)\n", prog->filter); + + unsigned int i; + for (i = 0; i <= BPF_MAXINSNS; ++i) { + const struct sock_filter stmt = + BPF_STMT(BPF_CLASS(i), i << 16); + big_filter[i] = stmt; + } + + prog->filter = big_filter; + prog->len = BPF_MAXINSNS + 1; + tprintf("seccomp(SECCOMP_SET_MODE_FILTER, %s, {len=%u, filter=[", + "SECCOMP_FILTER_FLAG_TSYNC|0xfffffffe", prog->len); + for (i = 0; i < BPF_MAXINSNS; ++i) { + if (i) + tprintf(", "); + switch(BPF_CLASS(i)) { + case BPF_LD: + tprintf("BPF_STMT(BPF_LD|BPF_W|BPF_IMM, %#x)", i << 16); + break; + case BPF_LDX: + tprintf("BPF_STMT(BPF_LDX|BPF_W|BPF_IMM, %#x)", i << 16); + break; + case BPF_ST: + tprintf("BPF_STMT(BPF_ST, %#x)", i << 16); + break; + case BPF_STX: + tprintf("BPF_STMT(BPF_STX, %#x)", i << 16); + break; + case BPF_ALU: + tprintf("BPF_STMT(BPF_ALU|BPF_K|BPF_ADD, %#x)", i << 16); + break; + case BPF_JMP: + tprintf("BPF_STMT(BPF_JMP|BPF_K|BPF_JA, %#x)", i << 16); + break; + case BPF_RET: + tprintf("BPF_STMT(BPF_RET|BPF_K, %#x" + " /* SECCOMP_RET_??? */)", i << 16); + break; + case BPF_MISC: + tprintf("BPF_STMT(BPF_MISC|BPF_TAX, %#x)", i << 16); + break; + } + } + tprintf(", ...]})"); + syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, -1, prog); + tprintf(" = -1 EINVAL (%m)\n"); + + prog->filter = filter; + prog->len = ARRAY_SIZE(filter_c); + + tprintf("seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=%u, filter=[", + prog->len); + + tprintf("BPF_STMT(BPF_LD|BPF_W|BPF_ABS, %#x), ", + (unsigned) offsetof(struct seccomp_data, nr)); + + PRINT_ALLOW_SYSCALL(close); + PRINT_ALLOW_SYSCALL(exit); + PRINT_ALLOW_SYSCALL(exit_group); + + PRINT_DENY_SYSCALL(sync, EBUSY), + PRINT_DENY_SYSCALL(setsid, EPERM), + + tprintf("%s]}) = 0\n+++ exited with 0 +++\n", kill_stmt_txt); + + if (syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 0, prog)) + perror_msg_and_skip("SECCOMP_SET_MODE_FILTER"); + + if (close(0) || close(1)) + _exit(77); + + _exit(0); +} + +#else + +SKIP_MAIN_UNDEFINED("__NR_seccomp && PR_SET_NO_NEW_PRIVS" + " && SECCOMP_SET_MODE_FILTER && SECCOMP_RET_ERRNO" + " && BPF_JUMP && BPF_STMT") + +#endif diff --git a/tests/seccomp-filter-v.test b/tests/seccomp-filter-v.test new file mode 100755 index 00000000..f873eb6f --- /dev/null +++ b/tests/seccomp-filter-v.test @@ -0,0 +1,6 @@ +#!/bin/sh + +# Check verbose decoding of seccomp SECCOMP_SET_MODE_FILTER. + +. "${srcdir=.}/init.sh" +run_strace_match_diff -v -e trace=seccomp -- 2.40.0