summaryrefslogtreecommitdiffstats
path: root/src/api.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:09:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:09:30 +0000
commit81749f1fe87e489c4e2e7408a0fae9370c3810b3 (patch)
tree2d1345a5762855b6577495d90ac134c4e92d7ff8 /src/api.c
parentInitial commit. (diff)
downloadlibseccomp-81749f1fe87e489c4e2e7408a0fae9370c3810b3.tar.xz
libseccomp-81749f1fe87e489c4e2e7408a0fae9370c3810b3.zip
Adding upstream version 2.5.5.upstream/2.5.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/api.c')
-rw-r--r--src/api.c733
1 files changed, 733 insertions, 0 deletions
diff --git a/src/api.c b/src/api.c
new file mode 100644
index 0000000..5cec088
--- /dev/null
+++ b/src/api.c
@@ -0,0 +1,733 @@
+/**
+ * Seccomp Library API
+ *
+ * Copyright (c) 2012,2013 Red Hat <pmoore@redhat.com>
+ * Author: Paul Moore <paul@paul-moore.com>
+ */
+
+/*
+ * 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 <http://www.gnu.org/licenses>.
+ */
+
+#include <endian.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <sys/ioctl.h>
+
+#include <seccomp.h>
+
+#include "arch.h"
+#include "db.h"
+#include "gen_pfc.h"
+#include "gen_bpf.h"
+#include "helper.h"
+#include "system.h"
+
+#define API __attribute__((visibility("default")))
+
+const struct scmp_version library_version = {
+ .major = SCMP_VER_MAJOR,
+ .minor = SCMP_VER_MINOR,
+ .micro = SCMP_VER_MICRO,
+};
+
+unsigned int seccomp_api_level = 0;
+
+/**
+ * Filter the error codes we send back to callers
+ * @param err the error code
+ *
+ * We consider error codes part of our API so we want to make sure we don't
+ * accidentally send an undocumented error code to our callers. This function
+ * helps with that.
+ *
+ */
+static int _rc_filter(int err)
+{
+ /* pass through success values */
+ if (err >= 0)
+ return err;
+
+ /* filter the error codes */
+ switch (err) {
+ case -EACCES:
+ /* NOTE: operation is not permitted by libseccomp */
+ case -ECANCELED:
+ /* NOTE: kernel level error that is beyond the control of
+ * libseccomp */
+ case -EDOM:
+ /* NOTE: failure due to arch/ABI */
+ case -EEXIST:
+ /* NOTE: operation failed due to existing rule or filter */
+ case -EINVAL:
+ /* NOTE: invalid input to the libseccomp API */
+ case -ENOENT:
+ /* NOTE: no matching entry found */
+ case -ENOMEM:
+ /* NOTE: unable to allocate enough memory to perform the
+ * requested operation */
+ case -EOPNOTSUPP:
+ /* NOTE: operation is not supported */
+ case -ESRCH:
+ /* NOTE: operation failed due to multi-threading */
+ return err;
+ default:
+ /* NOTE: this is the default "internal libseccomp error"
+ * error code, it is our catch-all */
+ return -EFAULT;
+ }
+}
+
+/**
+ * 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
+ *
+ * Attempt to validate the provided filter context. Returns zero if the
+ * context is valid, negative values on failure.
+ *
+ */
+static int _ctx_valid(const scmp_filter_ctx *ctx)
+{
+ return db_col_valid((struct db_filter_col *)ctx);
+}
+
+/**
+ * Validate a syscall number
+ * @param syscall the syscall number
+ *
+ * Attempt to perform basic syscall number validation. Returns zero of the
+ * syscall appears valid, negative values on failure.
+ *
+ */
+static int _syscall_valid(const struct db_filter_col *col, int syscall)
+{
+ /* syscall -1 is used by tracers to skip the syscall */
+ if (col->attr.api_tskip && syscall == -1)
+ return 0;
+ if (syscall <= -1 && syscall >= -99)
+ return -EINVAL;
+ return 0;
+}
+
+/**
+ * Update the API level
+ *
+ * This function performs a series of tests to determine what functionality is
+ * supported given the current running environment (kernel, etc.). It is
+ * important to note that this function only does meaningful checks the first
+ * time it is run, the resulting API level is cached after this first run and
+ * used for all subsequent calls. The API level value is returned.
+ *
+ */
+static unsigned int _seccomp_api_update(void)
+{
+ unsigned int level = 1;
+
+ /* if seccomp_api_level > 0 then it's already been set, we're done */
+ if (seccomp_api_level >= 1)
+ return seccomp_api_level;
+
+ /* NOTE: level 1 is the base level, start checking at 2 */
+
+ if (sys_chk_seccomp_syscall() &&
+ sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC) == 1)
+ level = 2;
+
+ if (level == 2 &&
+ sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_LOG) == 1 &&
+ sys_chk_seccomp_action(SCMP_ACT_LOG) == 1 &&
+ sys_chk_seccomp_action(SCMP_ACT_KILL_PROCESS) == 1)
+ level = 3;
+
+ if (level == 3 &&
+ sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_SPEC_ALLOW) == 1)
+ level = 4;
+
+ if (level == 4 &&
+ sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_NEW_LISTENER) == 1 &&
+ sys_chk_seccomp_action(SCMP_ACT_NOTIFY) == 1)
+ level = 5;
+
+ if (level == 5 &&
+ sys_chk_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC_ESRCH) == 1)
+ level = 6;
+
+ /* update the stored api level and return */
+ seccomp_api_level = level;
+ return seccomp_api_level;
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API const struct scmp_version *seccomp_version(void)
+{
+ return &library_version;
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API unsigned int seccomp_api_get(void)
+{
+ /* update the api level, if needed */
+ return _seccomp_api_update();
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_api_set(unsigned int level)
+{
+ switch (level) {
+ case 1:
+ sys_set_seccomp_syscall(false);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC, false);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_LOG, false);
+ sys_set_seccomp_action(SCMP_ACT_LOG, false);
+ sys_set_seccomp_action(SCMP_ACT_KILL_PROCESS, false);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_SPEC_ALLOW, false);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_NEW_LISTENER, false);
+ sys_set_seccomp_action(SCMP_ACT_NOTIFY, false);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC_ESRCH, false);
+ break;
+ case 2:
+ sys_set_seccomp_syscall(true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_LOG, false);
+ sys_set_seccomp_action(SCMP_ACT_LOG, false);
+ sys_set_seccomp_action(SCMP_ACT_KILL_PROCESS, false);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_SPEC_ALLOW, false);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_NEW_LISTENER, false);
+ sys_set_seccomp_action(SCMP_ACT_NOTIFY, false);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC_ESRCH, false);
+ break;
+ case 3:
+ sys_set_seccomp_syscall(true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_LOG, true);
+ sys_set_seccomp_action(SCMP_ACT_LOG, true);
+ sys_set_seccomp_action(SCMP_ACT_KILL_PROCESS, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_SPEC_ALLOW, false);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_NEW_LISTENER, false);
+ sys_set_seccomp_action(SCMP_ACT_NOTIFY, false);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC_ESRCH, false);
+ break;
+ case 4:
+ sys_set_seccomp_syscall(true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_LOG, true);
+ sys_set_seccomp_action(SCMP_ACT_LOG, true);
+ sys_set_seccomp_action(SCMP_ACT_KILL_PROCESS, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_SPEC_ALLOW, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_NEW_LISTENER, false);
+ sys_set_seccomp_action(SCMP_ACT_NOTIFY, false);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC_ESRCH, false);
+ break;
+ case 5:
+ sys_set_seccomp_syscall(true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_LOG, true);
+ sys_set_seccomp_action(SCMP_ACT_LOG, true);
+ sys_set_seccomp_action(SCMP_ACT_KILL_PROCESS, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_SPEC_ALLOW, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_NEW_LISTENER, true);
+ sys_set_seccomp_action(SCMP_ACT_NOTIFY, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC_ESRCH, false);
+ break;
+ case 6:
+ sys_set_seccomp_syscall(true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_LOG, true);
+ sys_set_seccomp_action(SCMP_ACT_LOG, true);
+ sys_set_seccomp_action(SCMP_ACT_KILL_PROCESS, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_SPEC_ALLOW, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_NEW_LISTENER, true);
+ sys_set_seccomp_action(SCMP_ACT_NOTIFY, true);
+ sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC_ESRCH, true);
+ break;
+ default:
+ return _rc_filter(-EINVAL);
+ }
+
+ seccomp_api_level = level;
+ return _rc_filter(0);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API scmp_filter_ctx seccomp_init(uint32_t def_action)
+{
+ /* force a runtime api level detection */
+ _seccomp_api_update();
+
+ if (db_col_action_valid(NULL, def_action) < 0)
+ return NULL;
+
+ return db_col_init(def_action);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_reset(scmp_filter_ctx ctx, uint32_t def_action)
+{
+ struct db_filter_col *col = (struct db_filter_col *)ctx;
+
+ /* a NULL filter context indicates we are resetting the global state */
+ if (ctx == NULL) {
+ /* reset the global state and redetermine the api level */
+ sys_reset_state();
+ _seccomp_api_update();
+ return _rc_filter(0);
+ }
+ /* ensure the default action is valid */
+ if (db_col_action_valid(NULL, def_action) < 0)
+ return _rc_filter(-EINVAL);
+
+ /* reset the filter */
+ return _rc_filter(db_col_reset(col, def_action));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API void seccomp_release(scmp_filter_ctx ctx)
+{
+ db_col_release((struct db_filter_col *)ctx);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_merge(scmp_filter_ctx ctx_dst, scmp_filter_ctx ctx_src)
+{
+ struct db_filter_col *col_dst = (struct db_filter_col *)ctx_dst;
+ struct db_filter_col *col_src = (struct db_filter_col *)ctx_src;
+
+ if (db_col_valid(col_dst) || db_col_valid(col_src))
+ return _rc_filter(-EINVAL);
+
+ /* NOTE: only the default action, NNP, and TSYNC settings must match */
+ if ((col_dst->attr.act_default != col_src->attr.act_default) ||
+ (col_dst->attr.nnp_enable != col_src->attr.nnp_enable) ||
+ (col_dst->attr.tsync_enable != col_src->attr.tsync_enable))
+ return _rc_filter(-EINVAL);
+
+ return _rc_filter(db_col_merge(col_dst, col_src));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API uint32_t seccomp_arch_resolve_name(const char *arch_name)
+{
+ const struct arch_def *arch;
+
+ if (arch_name == NULL)
+ return 0;
+
+ arch = arch_def_lookup_name(arch_name);
+ if (arch == NULL)
+ return 0;
+
+ return arch->token;
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API uint32_t seccomp_arch_native(void)
+{
+ return arch_def_native->token;
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_arch_exist(const scmp_filter_ctx ctx, uint32_t arch_token)
+{
+ struct db_filter_col *col = (struct db_filter_col *)ctx;
+
+ if (arch_token == 0)
+ arch_token = arch_def_native->token;
+
+ if (arch_valid(arch_token))
+ return _rc_filter(-EINVAL);
+
+ return _rc_filter((db_col_arch_exist(col, arch_token) ? 0 : -EEXIST));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_arch_add(scmp_filter_ctx ctx, uint32_t arch_token)
+{
+ const struct arch_def *arch;
+ struct db_filter_col *col = (struct db_filter_col *)ctx;
+
+ if (arch_token == 0)
+ arch_token = arch_def_native->token;
+
+ arch = arch_def_lookup(arch_token);
+ if (arch == NULL)
+ return _rc_filter(-EINVAL);
+ if (db_col_arch_exist(col, arch_token))
+ return _rc_filter(-EEXIST);
+
+ return _rc_filter(db_col_db_new(col, arch));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_arch_remove(scmp_filter_ctx ctx, uint32_t arch_token)
+{
+ struct db_filter_col *col = (struct db_filter_col *)ctx;
+
+ if (arch_token == 0)
+ arch_token = arch_def_native->token;
+
+ if (arch_valid(arch_token))
+ return _rc_filter(-EINVAL);
+ if (db_col_arch_exist(col, arch_token) != -EEXIST)
+ return _rc_filter(-EEXIST);
+
+ return _rc_filter(db_col_db_remove(col, arch_token));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+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;
+
+ 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 */
+API int seccomp_attr_get(const scmp_filter_ctx ctx,
+ enum scmp_filter_attr attr, uint32_t *value)
+{
+ if (_ctx_valid(ctx))
+ return _rc_filter(-EINVAL);
+
+ return _rc_filter(db_col_attr_get((const struct db_filter_col *)ctx,
+ attr, value));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_attr_set(scmp_filter_ctx ctx,
+ enum scmp_filter_attr attr, uint32_t value)
+{
+ if (_ctx_valid(ctx))
+ return _rc_filter(-EINVAL);
+
+ return _rc_filter(db_col_attr_set((struct db_filter_col *)ctx,
+ attr, value));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API char *seccomp_syscall_resolve_num_arch(uint32_t arch_token, int num)
+{
+ const struct arch_def *arch;
+ const char *name;
+
+ if (arch_token == 0)
+ arch_token = arch_def_native->token;
+ if (arch_valid(arch_token))
+ return NULL;
+ arch = arch_def_lookup(arch_token);
+ if (arch == NULL)
+ return NULL;
+
+ name = arch_syscall_resolve_num(arch, num);
+ if (name == NULL)
+ return NULL;
+
+ return strdup(name);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_syscall_resolve_name_arch(uint32_t arch_token, const char *name)
+{
+ const struct arch_def *arch;
+
+ if (name == NULL)
+ return __NR_SCMP_ERROR;
+
+ if (arch_token == 0)
+ arch_token = arch_def_native->token;
+ if (arch_valid(arch_token))
+ return __NR_SCMP_ERROR;
+ arch = arch_def_lookup(arch_token);
+ if (arch == NULL)
+ return __NR_SCMP_ERROR;
+
+ return arch_syscall_resolve_name(arch, name);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_syscall_resolve_name_rewrite(uint32_t arch_token,
+ const char *name)
+{
+ int rc;
+ int syscall;
+ const struct arch_def *arch;
+
+ if (name == NULL)
+ return __NR_SCMP_ERROR;
+
+ if (arch_token == 0)
+ arch_token = arch_def_native->token;
+ if (arch_valid(arch_token))
+ return __NR_SCMP_ERROR;
+ arch = arch_def_lookup(arch_token);
+ if (arch == NULL)
+ return __NR_SCMP_ERROR;
+
+ syscall = arch_syscall_resolve_name(arch, name);
+ if (syscall == __NR_SCMP_ERROR)
+ return __NR_SCMP_ERROR;
+ rc = arch_syscall_rewrite(arch, &syscall);
+ if (rc == -EDOM)
+ /* if we can't rewrite the syscall, just pass it through */
+ return syscall;
+ else if (rc < 0)
+ return __NR_SCMP_ERROR;
+
+ return syscall;
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_syscall_resolve_name(const char *name)
+{
+ return seccomp_syscall_resolve_name_arch(SCMP_ARCH_NATIVE, name);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_syscall_priority(scmp_filter_ctx ctx,
+ int syscall, uint8_t priority)
+{
+ struct db_filter_col *col = (struct db_filter_col *)ctx;
+
+ if (db_col_valid(col) || _syscall_valid(col, syscall))
+ return _rc_filter(-EINVAL);
+
+ return _rc_filter(db_col_syscall_priority(col, syscall, priority));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_rule_add_array(scmp_filter_ctx ctx,
+ uint32_t action, int syscall,
+ unsigned int arg_cnt,
+ const struct scmp_arg_cmp *arg_array)
+{
+ int rc;
+ struct db_filter_col *col = (struct db_filter_col *)ctx;
+
+ if (arg_cnt > ARG_COUNT_MAX)
+ return _rc_filter(-EINVAL);
+ if (arg_cnt > 0 && arg_array == NULL)
+ return _rc_filter(-EINVAL);
+
+ if (db_col_valid(col) || _syscall_valid(col, syscall))
+ return _rc_filter(-EINVAL);
+
+ rc = db_col_action_valid(col, action);
+ if (rc < 0)
+ return _rc_filter(rc);
+ if (action == col->attr.act_default)
+ return _rc_filter(-EACCES);
+
+ return _rc_filter(db_col_rule_add(col, 0, action,
+ syscall, arg_cnt, arg_array));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_rule_add(scmp_filter_ctx ctx,
+ uint32_t action, int syscall,
+ unsigned int arg_cnt, ...)
+{
+ int rc;
+ int iter;
+ struct scmp_arg_cmp arg_array[ARG_COUNT_MAX];
+ va_list arg_list;
+
+ /* arg_cnt is unsigned, so no need to check the lower bound */
+ if (arg_cnt > ARG_COUNT_MAX)
+ return _rc_filter(-EINVAL);
+
+ va_start(arg_list, arg_cnt);
+ for (iter = 0; iter < arg_cnt; ++iter)
+ arg_array[iter] = va_arg(arg_list, struct scmp_arg_cmp);
+ rc = seccomp_rule_add_array(ctx, action, syscall, arg_cnt, arg_array);
+ va_end(arg_list);
+
+ return _rc_filter(rc);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_rule_add_exact_array(scmp_filter_ctx ctx,
+ uint32_t action, int syscall,
+ unsigned int arg_cnt,
+ const struct scmp_arg_cmp *arg_array)
+{
+ int rc;
+ struct db_filter_col *col = (struct db_filter_col *)ctx;
+
+ if (arg_cnt > ARG_COUNT_MAX)
+ return _rc_filter(-EINVAL);
+ if (arg_cnt > 0 && arg_array == NULL)
+ return _rc_filter(-EINVAL);
+
+ if (db_col_valid(col) || _syscall_valid(col, syscall))
+ return _rc_filter(-EINVAL);
+
+ rc = db_col_action_valid(col, action);
+ if (rc < 0)
+ return _rc_filter(rc);
+ if (action == col->attr.act_default)
+ return _rc_filter(-EACCES);
+
+ if (col->filter_cnt > 1)
+ return _rc_filter(-EOPNOTSUPP);
+
+ return _rc_filter(db_col_rule_add(col, 1, action,
+ syscall, arg_cnt, arg_array));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_rule_add_exact(scmp_filter_ctx ctx,
+ uint32_t action, int syscall,
+ unsigned int arg_cnt, ...)
+{
+ int rc;
+ int iter;
+ struct scmp_arg_cmp arg_array[ARG_COUNT_MAX];
+ va_list arg_list;
+
+ /* arg_cnt is unsigned, so no need to check the lower bound */
+ if (arg_cnt > ARG_COUNT_MAX)
+ return _rc_filter(-EINVAL);
+
+ va_start(arg_list, arg_cnt);
+ for (iter = 0; iter < arg_cnt; ++iter)
+ arg_array[iter] = va_arg(arg_list, struct scmp_arg_cmp);
+ rc = seccomp_rule_add_exact_array(ctx,
+ action, syscall, arg_cnt, arg_array);
+ va_end(arg_list);
+
+ return _rc_filter(rc);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_notify_alloc(struct seccomp_notif **req,
+ struct seccomp_notif_resp **resp)
+{
+ /* force a runtime api level detection */
+ _seccomp_api_update();
+
+ return _rc_filter(sys_notify_alloc(req, resp));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API void seccomp_notify_free(struct seccomp_notif *req,
+ struct seccomp_notif_resp *resp)
+{
+ if (req)
+ free(req);
+ if (resp)
+ free(resp);
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_notify_receive(int fd, struct seccomp_notif *req)
+{
+ return _rc_filter(sys_notify_receive(fd, req));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_notify_respond(int fd, struct seccomp_notif_resp *resp)
+{
+ return _rc_filter(sys_notify_respond(fd, resp));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_notify_id_valid(int fd, uint64_t id)
+{
+ /* force a runtime api level detection */
+ _seccomp_api_update();
+
+ return _rc_filter(sys_notify_id_valid(fd, id));
+}
+
+/* NOTE - function header comment in include/seccomp.h */
+API int seccomp_notify_fd(const scmp_filter_ctx ctx)
+{
+ /* NOTE: for historical reasons, and possibly future use, we require a
+ * valid filter context even though we don't actual use it here; the
+ * api update is also not strictly necessary, but keep it for now */
+
+ /* force a runtime api level detection */
+ _seccomp_api_update();
+
+ if (_ctx_valid(ctx))
+ return _rc_filter(-EINVAL);
+
+ return _rc_filter(sys_notify_fd());
+}
+
+/* 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;
+
+ 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(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_sys(col, -errno);
+
+ return 0;
+}