From 34bf78abc9567b66c72dbe67e7f243072162a25f Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Sat, 6 Jun 2020 16:36:38 -0400 Subject: [PATCH] api: add the SCMP_FLTATR_API_SYSRAWRC filter attribute See the manpage additions as part of this patch, but the basic idea is that when this attribute is non-zero we make every effort to convey the system's errno value back to the caller when something goes wrong in libc or the kernel. It is important to note from a support perspective that our ability to support callers who make use of this attribute will be diminished as the libc and kernel errno values are beyond libseccomp's control. If the attribute is zero, the library hides all of the system failures under -ECANCELED. Acked-by: Tom Hromatka Signed-off-by: Paul Moore --- doc/man/man3/seccomp_attr_set.3 | 8 +++- doc/man/man3/seccomp_export_bpf.3 | 7 +++- doc/man/man3/seccomp_load.3 | 7 +++- doc/man/man3/seccomp_notify_alloc.3 | 7 ++-- include/seccomp.h.in | 1 + src/api.c | 40 +++++++++++++++--- src/db.c | 26 ++++++++++++ src/db.h | 4 ++ src/gen_pfc.c | 6 +-- src/python/libseccomp.pxd | 1 + src/python/seccomp.pyx | 6 +++ src/system.c | 5 ++- src/system.h | 2 +- tests/.gitignore | 1 + tests/13-basic-attrs.c | 22 ++++++++++ tests/13-basic-attrs.py | 3 ++ tests/57-basic-rawsysrc.c | 64 +++++++++++++++++++++++++++++ tests/57-basic-rawsysrc.py | 46 +++++++++++++++++++++ tests/57-basic-rawsysrc.tests | 11 +++++ tests/Makefile.am | 6 ++- 20 files changed, 254 insertions(+), 19 deletions(-) create mode 100644 tests/57-basic-rawsysrc.c create mode 100755 tests/57-basic-rawsysrc.py create mode 100644 tests/57-basic-rawsysrc.tests diff --git a/doc/man/man3/seccomp_attr_set.3 b/doc/man/man3/seccomp_attr_set.3 index a23d8750..61834931 100644 --- a/doc/man/man3/seccomp_attr_set.3 +++ b/doc/man/man3/seccomp_attr_set.3 @@ -1,4 +1,4 @@ -.TH "seccomp_attr_set" 3 "30 May 2020" "paul@paul-moore.com" "libseccomp Documentation" +.TH "seccomp_attr_set" 3 "06 June 2020" "paul@paul-moore.com" "libseccomp Documentation" .\" ////////////////////////////////////////////////////////////////////////// .SH NAME .\" ////////////////////////////////////////////////////////////////////////// @@ -100,6 +100,12 @@ A flag to disable Speculative Store Bypass mitigations for this filter. Defaults to off ( .I value == 0). +.TP +.B SCMP_FLTATR_API_SYSRAWRC +A flag to specify if libseccomp should pass system error codes back to the +caller instead of the default -ECANCELED. Defaults to off ( +.I value +== 0). .\" ////////////////////////////////////////////////////////////////////////// .SH RETURN VALUE .\" ////////////////////////////////////////////////////////////////////////// diff --git a/doc/man/man3/seccomp_export_bpf.3 b/doc/man/man3/seccomp_export_bpf.3 index 68e735fb..98b35729 100644 --- a/doc/man/man3/seccomp_export_bpf.3 +++ b/doc/man/man3/seccomp_export_bpf.3 @@ -49,7 +49,7 @@ Return zero on success or one of the following error codes on failure: .TP .B -ECANCELED -There was a kernel failure beyond the control of the library. +There was a system failure beyond the control of the library. .TP .B -EFAULT Internal libseccomp failure. @@ -59,6 +59,11 @@ Invalid input, either the context or architecture token is invalid. .TP .B -ENOMEM The library was unable to allocate enough memory. +.P +If the \fISCMP_FLTATR_API_SYSRAWRC\fP filter attribute is non-zero then +additional error codes may be returned to the caller; these additional error +codes are the negative \fIerrno\fP values returned by the system. Unfortunately +libseccomp can make no guarantees about these return values. .\" ////////////////////////////////////////////////////////////////////////// .SH EXAMPLES .\" ////////////////////////////////////////////////////////////////////////// diff --git a/doc/man/man3/seccomp_load.3 b/doc/man/man3/seccomp_load.3 index dcca7f5b..8e2e6f0a 100644 --- a/doc/man/man3/seccomp_load.3 +++ b/doc/man/man3/seccomp_load.3 @@ -39,7 +39,7 @@ is "stricter" than Returns zero on success or one of the following error codes on failure: .TP .B -ECANCELED -There was a kernel failure beyond the control of the library. +There was a system failure beyond the control of the library. .TP .B -EFAULT Internal libseccomp failure. @@ -52,6 +52,11 @@ The library was unable to allocate enough memory. .TP .B -ESRCH Unable to load the filter due to thread issues. +.P +If the \fISCMP_FLTATR_API_SYSRAWRC\fP filter attribute is non-zero then +additional error codes may be returned to the caller; these additional error +codes are the negative \fIerrno\fP values returned by the system. Unfortunately +libseccomp can make no guarantees about these return values. .\" ////////////////////////////////////////////////////////////////////////// .SH EXAMPLES .\" ////////////////////////////////////////////////////////////////////////// diff --git a/doc/man/man3/seccomp_notify_alloc.3 b/doc/man/man3/seccomp_notify_alloc.3 index 48c4599b..50c89706 100644 --- a/doc/man/man3/seccomp_notify_alloc.3 +++ b/doc/man/man3/seccomp_notify_alloc.3 @@ -47,7 +47,7 @@ this response corresponds to. .P The .BR seccomp_notify_id_valid () -function checks to see if the syscall from a particualr notification request is +function checks to see if the syscall from a particular notification request is still valid, i.e. if the task is still alive. See NOTES below for details on race conditions. .P @@ -70,11 +70,12 @@ The .BR seccomp_notify_receive (), and .BR seccomp_notify_respond () -functions return zero on success or one of the following error codes on +functions return zero on success, or one of the following error codes on failure: .TP .B -ECANCELED -There was a kernel failure beyond the control of the library. +There was a system failure beyond the control of the library, check the +\fIerrno\fP value for more information. .TP .B -EFAULT Internal libseccomp failure. diff --git a/include/seccomp.h.in b/include/seccomp.h.in index 17c90b7b..c78846b5 100644 --- a/include/seccomp.h.in +++ b/include/seccomp.h.in @@ -76,6 +76,7 @@ enum scmp_filter_attr { * 2 - binary tree sorted by syscall * number */ + SCMP_FLTATR_API_SYSRAWRC = 9, /**< return the system return codes */ _SCMP_FLTATR_MAX, }; diff --git a/src/api.c b/src/api.c index e01b196f..b4f0c642 100644 --- a/src/api.c +++ b/src/api.c @@ -93,6 +93,27 @@ static int _rc_filter(int err) } } +/** + * Filter the system error codes we send back to callers + * @param col the filter collection + * @param err the error code + * + * This is similar to _rc_filter(), but it first checks the filter attribute + * to determine if we should be filtering the return codes. + * + */ +static int _rc_filter_sys(struct db_filter_col *col, int err) +{ + /* pass through success values */ + if (err >= 0) + return err; + + /* pass the return code if the SCMP_FLTATR_API_SYSRAWRC is true */ + if (db_col_attr_read(col, SCMP_FLTATR_API_SYSRAWRC)) + return err; + return -ECANCELED; +} + /** * Validate a filter context * @param ctx the filter context @@ -355,12 +376,14 @@ API int seccomp_arch_remove(scmp_filter_ctx ctx, uint32_t arch_token) API int seccomp_load(const scmp_filter_ctx ctx) { struct db_filter_col *col; + bool rawrc; if (_ctx_valid(ctx)) return _rc_filter(-EINVAL); col = (struct db_filter_col *)ctx; - return _rc_filter(sys_filter_load(col)); + rawrc = db_col_attr_read(col, SCMP_FLTATR_API_SYSRAWRC); + return _rc_filter(sys_filter_load(col, rawrc)); } /* NOTE - function header comment in include/seccomp.h */ @@ -638,28 +661,35 @@ API int seccomp_notify_fd(const scmp_filter_ctx ctx) /* NOTE - function header comment in include/seccomp.h */ API int seccomp_export_pfc(const scmp_filter_ctx ctx, int fd) { + int rc; + struct db_filter_col *col; + if (_ctx_valid(ctx)) return _rc_filter(-EINVAL); + col = (struct db_filter_col *)ctx; - return _rc_filter(gen_pfc_generate((struct db_filter_col *)ctx, fd)); + rc = gen_pfc_generate(col, fd); + return _rc_filter_sys(col, rc); } /* NOTE - function header comment in include/seccomp.h */ API int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd) { int rc; + struct db_filter_col *col; struct bpf_program *program; if (_ctx_valid(ctx)) return _rc_filter(-EINVAL); + col = (struct db_filter_col *)ctx; - rc = gen_bpf_generate((struct db_filter_col *)ctx, &program); + rc = gen_bpf_generate(col, &program); if (rc < 0) return _rc_filter(rc); rc = write(fd, program->blks, BPF_PGM_SIZE(program)); gen_bpf_release(program); if (rc < 0) - return _rc_filter(-ECANCELED); + return _rc_filter_sys(col, -errno); - return _rc_filter(0); + return 0; } diff --git a/src/db.c b/src/db.c index ebf6ad0e..f0e00063 100644 --- a/src/db.c +++ b/src/db.c @@ -1071,6 +1071,7 @@ int db_col_reset(struct db_filter_col *col, uint32_t def_action) col->attr.log_enable = 0; col->attr.spec_allow = 0; col->attr.optimize = 1; + col->attr.api_sysrawrc = 0; /* set the state */ col->state = _DB_STA_VALID; @@ -1316,6 +1317,9 @@ int db_col_attr_get(const struct db_filter_col *col, case SCMP_FLTATR_CTL_OPTIMIZE: *value = col->attr.optimize; break; + case SCMP_FLTATR_API_SYSRAWRC: + *value = col->attr.api_sysrawrc; + break; default: rc = -EINVAL; break; @@ -1324,6 +1328,25 @@ int db_col_attr_get(const struct db_filter_col *col, return rc; } +/** + * Get a filter attribute + * @param col the seccomp filter collection + * @param attr the filter attribute + * + * Returns the requested filter attribute value with zero on any error. + * Special care must be given with this function as error conditions can be + * hidden from the caller. + * + */ +uint32_t db_col_attr_read(const struct db_filter_col *col, + enum scmp_filter_attr attr) +{ + uint32_t value = 0; + + db_col_attr_get(col, attr, &value); + return value; +} + /** * Set a filter attribute * @param col the seccomp filter collection @@ -1402,6 +1425,9 @@ int db_col_attr_set(struct db_filter_col *col, break; } break; + case SCMP_FLTATR_API_SYSRAWRC: + col->attr.api_sysrawrc = (value ? 1 : 0); + break; default: rc = -EINVAL; break; diff --git a/src/db.h b/src/db.h index ae485eef..b96b1049 100644 --- a/src/db.h +++ b/src/db.h @@ -120,6 +120,8 @@ struct db_filter_attr { uint32_t spec_allow; /* SCMP_FLTATR_CTL_OPTIMIZE related attributes */ uint32_t optimize; + /* return the raw system return codes */ + uint32_t api_sysrawrc; }; struct db_filter { @@ -191,6 +193,8 @@ int db_col_arch_exist(struct db_filter_col *col, uint32_t arch_token); int db_col_attr_get(const struct db_filter_col *col, enum scmp_filter_attr attr, uint32_t *value); +uint32_t db_col_attr_read(const struct db_filter_col *col, + enum scmp_filter_attr attr); int db_col_attr_set(struct db_filter_col *col, enum scmp_filter_attr attr, uint32_t value); diff --git a/src/gen_pfc.c b/src/gen_pfc.c index 9d86e06d..405f0807 100644 --- a/src/gen_pfc.c +++ b/src/gen_pfc.c @@ -464,7 +464,7 @@ static int _gen_pfc_arch(const struct db_filter_col *col, * * This function generates a pseudo filter code representation of the given * filter collection and writes it to the given fd. Returns zero on success, - * negative values on failure. + * negative errno values on failure. * */ int gen_pfc_generate(const struct db_filter_col *col, int fd) @@ -475,11 +475,11 @@ int gen_pfc_generate(const struct db_filter_col *col, int fd) newfd = dup(fd); if (newfd < 0) - return -ECANCELED; + return -errno; fds = fdopen(newfd, "a"); if (fds == NULL) { close(newfd); - return -ECANCELED; + return -errno; } /* generate the pfc */ diff --git a/src/python/libseccomp.pxd b/src/python/libseccomp.pxd index b9703180..0629bf1e 100644 --- a/src/python/libseccomp.pxd +++ b/src/python/libseccomp.pxd @@ -62,6 +62,7 @@ cdef extern from "seccomp.h": SCMP_FLTATR_CTL_LOG SCMP_FLTATR_CTL_SSB SCMP_FLTATR_CTL_OPTIMIZE + SCMP_FLTATR_API_SYSRAWRC cdef enum scmp_compare: SCMP_CMP_NE diff --git a/src/python/seccomp.pyx b/src/python/seccomp.pyx index d7c1bb12..13aa6692 100644 --- a/src/python/seccomp.pyx +++ b/src/python/seccomp.pyx @@ -318,6 +318,11 @@ cdef class Attr: CTL_TSKIP - allow rules with a -1 syscall number CTL_LOG - log not-allowed actions CTL_SSB - disable SSB mitigations + CTL_OPTIMIZE - the filter's optimization level: + 0: currently unused + 1: rules weighted by priority and complexity (DEFAULT) + 2: binary tree sorted by syscall number + API_SYSRAWRC - return the raw syscall codes """ ACT_DEFAULT = libseccomp.SCMP_FLTATR_ACT_DEFAULT ACT_BADARCH = libseccomp.SCMP_FLTATR_ACT_BADARCH @@ -327,6 +332,7 @@ cdef class Attr: CTL_LOG = libseccomp.SCMP_FLTATR_CTL_LOG CTL_SSB = libseccomp.SCMP_FLTATR_CTL_SSB CTL_OPTIMIZE = libseccomp.SCMP_FLTATR_CTL_OPTIMIZE + API_SYSRAWRC = libseccomp.SCMP_FLTATR_API_SYSRAWRC cdef class Arg: """ Python object representing a SyscallFilter syscall argument. diff --git a/src/system.c b/src/system.c index 737558e5..741a74ec 100644 --- a/src/system.c +++ b/src/system.c @@ -291,6 +291,7 @@ void sys_set_seccomp_flag(int flag, bool enable) /** * Loads the filter into the kernel * @param col the filter collection + * @param rawrc pass the raw return code if true * * This function loads the given seccomp filter context into the kernel. If * the filter was loaded correctly, the kernel will be enforcing the filter @@ -298,7 +299,7 @@ void sys_set_seccomp_flag(int flag, bool enable) * error. * */ -int sys_filter_load(struct db_filter_col *col) +int sys_filter_load(struct db_filter_col *col, bool rawrc) { int rc; struct bpf_program *prgm = NULL; @@ -343,7 +344,7 @@ int sys_filter_load(struct db_filter_col *col) if (rc == -ESRCH) return -ESRCH; if (rc < 0) - return -ECANCELED; + return (rawrc ? -errno : -ECANCELED); return rc; } diff --git a/src/system.h b/src/system.h index d183b5e2..7517c71d 100644 --- a/src/system.h +++ b/src/system.h @@ -188,7 +188,7 @@ void sys_set_seccomp_action(uint32_t action, bool enable); int sys_chk_seccomp_flag(int flag); void sys_set_seccomp_flag(int flag, bool enable); -int sys_filter_load(struct db_filter_col *col); +int sys_filter_load(struct db_filter_col *col, bool rawrc); int sys_notify_alloc(struct seccomp_notif **req, struct seccomp_notif_resp **resp); diff --git a/tests/.gitignore b/tests/.gitignore index b51acc34..59eb15c0 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -62,3 +62,4 @@ util.pyc 54-live-binary_tree 55-basic-pfc_binary_tree 56-basic-iterate_syscalls +57-basic-rawsysrc diff --git a/tests/13-basic-attrs.c b/tests/13-basic-attrs.c index e7b14f02..e3c5881d 100644 --- a/tests/13-basic-attrs.c +++ b/tests/13-basic-attrs.c @@ -120,6 +120,28 @@ int main(int argc, char *argv[]) goto out; } + rc = seccomp_attr_set(ctx, SCMP_FLTATR_CTL_OPTIMIZE, 2); + if (rc != 0) + goto out; + rc = seccomp_attr_get(ctx, SCMP_FLTATR_CTL_OPTIMIZE, &val); + if (rc != 0) + goto out; + if (val != 2) { + rc = -1; + goto out; + } + + rc = seccomp_attr_set(ctx, SCMP_FLTATR_API_SYSRAWRC, 1); + if (rc != 0) + goto out; + rc = seccomp_attr_get(ctx, SCMP_FLTATR_API_SYSRAWRC, &val); + if (rc != 0) + goto out; + if (val != 1) { + rc = -1; + goto out; + } + rc = 0; out: seccomp_release(ctx); diff --git a/tests/13-basic-attrs.py b/tests/13-basic-attrs.py index 0435ded9..48c25a06 100755 --- a/tests/13-basic-attrs.py +++ b/tests/13-basic-attrs.py @@ -58,6 +58,9 @@ def test(): f.set_attr(Attr.CTL_OPTIMIZE, 2) if f.get_attr(Attr.CTL_OPTIMIZE) != 2: raise RuntimeError("Failed getting Attr.CTL_OPTIMIZE") + f.set_attr(Attr.API_SYSRAWRC, 1) + if f.get_attr(Attr.API_SYSRAWRC) != 1: + raise RuntimeError("Failed getting Attr.API_SYSRAWRC") test() diff --git a/tests/57-basic-rawsysrc.c b/tests/57-basic-rawsysrc.c new file mode 100644 index 00000000..4248c7aa --- /dev/null +++ b/tests/57-basic-rawsysrc.c @@ -0,0 +1,64 @@ +/** + * Seccomp Library test program + * + * Copyright (c) 2020 Cisco Systems, Inc. + * Author: Paul Moore + */ + +/* + * This library is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License as + * published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see . + */ + +#include +#include +#include +#include +#include + +#include + +#include "util.h" + +int main(int argc, char *argv[]) +{ + int rc; + int fd; + scmp_filter_ctx ctx = NULL; + + rc = seccomp_api_set(3); + if (rc != 0) + return EOPNOTSUPP; + + ctx = seccomp_init(SCMP_ACT_ALLOW); + if (ctx == NULL) { + rc = ENOMEM; + goto out; + } + + rc = seccomp_attr_set(ctx, SCMP_FLTATR_API_SYSRAWRC, 1); + if (rc != 0) + goto out; + + /* we must use a closed/invalid fd for this to work */ + fd = dup(2); + close(fd); + rc = seccomp_export_pfc(ctx, fd); + if (rc == -EBADF) + rc = 0; + else + rc = -1; + +out: + seccomp_release(ctx); + return (rc < 0 ? -rc : rc); +} diff --git a/tests/57-basic-rawsysrc.py b/tests/57-basic-rawsysrc.py new file mode 100755 index 00000000..a88461a4 --- /dev/null +++ b/tests/57-basic-rawsysrc.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +# +# Seccomp Library test program +# +# Copyright (c) 2020 Cisco Systems, Inc. +# Author: Paul Moore +# + +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of version 2.1 of the GNU Lesser General Public License as +# published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, see . +# + +import argparse +import sys +import os + +import util + +from seccomp import * + +def test(): + # this test really isn't conclusive, but considering how python does error + # handling it may be the best we can do + f = SyscallFilter(ALLOW) + dummy = open("/dev/null", "w") + os.close(dummy.fileno()) + try: + f = f.export_pfc(dummy) + except RuntimeError: + pass + +test() + +# kate: syntax python; +# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off; diff --git a/tests/57-basic-rawsysrc.tests b/tests/57-basic-rawsysrc.tests new file mode 100644 index 00000000..fe716323 --- /dev/null +++ b/tests/57-basic-rawsysrc.tests @@ -0,0 +1,11 @@ +# +# libseccomp regression test automation data +# +# Copyright (c) 2020 Cisco Systems, Inc. +# Author: Paul Moore +# + +test type: basic + +# Test command +57-basic-rawsysrc diff --git a/tests/Makefile.am b/tests/Makefile.am index 629b910f..1765eeca 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -95,7 +95,8 @@ check_PROGRAMS = \ 53-sim-binary_tree \ 54-live-binary_tree \ 55-basic-pfc_binary_tree \ - 56-basic-iterate_syscalls + 56-basic-iterate_syscalls \ + 57-basic-rawsysrc EXTRA_DIST_TESTPYTHON = \ util.py \ @@ -210,7 +211,8 @@ EXTRA_DIST_TESTCFGS = \ 53-sim-binary_tree.tests \ 54-live-binary_tree.tests \ 55-basic-pfc_binary_tree.tests \ - 56-basic-iterate_syscalls.tests + 56-basic-iterate_syscalls.tests \ + 57-basic-rawsysrc.tests EXTRA_DIST_TESTSCRIPTS = \ 38-basic-pfc_coverage.sh 38-basic-pfc_coverage.pfc \