Bundle libseccomp 2.3.1
[linux-seccomp.git] / libseccomp / src / arch-x86.c
diff --git a/libseccomp/src/arch-x86.c b/libseccomp/src/arch-x86.c
new file mode 100644 (file)
index 0000000..8542079
--- /dev/null
@@ -0,0 +1,344 @@
+/**
+ * Enhanced Seccomp x86 Specific Code
+ *
+ * Copyright (c) 2012,2016 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 <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <linux/audit.h>
+
+#include "arch.h"
+#include "arch-x86.h"
+
+/* x86 syscall numbers */
+#define __x86_NR_socketcall            102
+#define __x86_NR_ipc                   117
+
+const struct arch_def arch_def_x86 = {
+       .token = SCMP_ARCH_X86,
+       .token_bpf = AUDIT_ARCH_I386,
+       .size = ARCH_SIZE_32,
+       .endian = ARCH_ENDIAN_LITTLE,
+       .syscall_resolve_name = x86_syscall_resolve_name,
+       .syscall_resolve_num = x86_syscall_resolve_num,
+       .syscall_rewrite = x86_syscall_rewrite,
+       .rule_add = x86_rule_add,
+};
+
+/**
+ * Convert a multiplexed pseudo socket syscall into a direct syscall
+ * @param socketcall the multiplexed pseudo syscall number
+ *
+ * Return the related direct syscall number, __NR_SCMP_UNDEF is there is
+ * no related syscall, or __NR_SCMP_ERROR otherwise.
+ *
+ */
+int _x86_sock_demux(int socketcall)
+{
+       switch (socketcall) {
+       case -101:
+               /* socket */
+               return 359;
+       case -102:
+               /* bind */
+               return 361;
+       case -103:
+               /* connect */
+               return 362;
+       case -104:
+               /* listen */
+               return 363;
+       case -105:
+               /* accept - not defined */
+               return __NR_SCMP_UNDEF;
+       case -106:
+               /* getsockname */
+               return 367;
+       case -107:
+               /* getpeername */
+               return 368;
+       case -108:
+               /* socketpair */
+               return 360;
+       case -109:
+               /* send - not defined */
+               return __NR_SCMP_UNDEF;
+       case -110:
+               /* recv - not defined */
+               return __NR_SCMP_UNDEF;
+       case -111:
+               /* sendto */
+               return 369;
+       case -112:
+               /* recvfrom */
+               return 371;
+       case -113:
+               /* shutdown */
+               return 373;
+       case -114:
+               /* setsockopt */
+               return 366;
+       case -115:
+               /* getsockopt */
+               return 365;
+       case -116:
+               /* sendmsg */
+               return 370;
+       case -117:
+               /* recvmsg */
+               return 372;
+       case -118:
+               /* accept4 */
+               return 364;
+       case -119:
+               /* recvmmsg */
+               return 337;
+       case -120:
+               /* sendmmsg */
+               return 345;
+       }
+
+       return __NR_SCMP_ERROR;
+}
+
+/**
+ * Convert a direct socket syscall into multiplexed pseudo socket syscall
+ * @param syscall the direct syscall
+ *
+ * Return the related multiplexed pseduo syscall number, __NR_SCMP_UNDEF is
+ * there is no related pseudo syscall, or __NR_SCMP_ERROR otherwise.
+ *
+ */
+int _x86_sock_mux(int syscall)
+{
+       switch (syscall) {
+       case 337:
+               /* recvmmsg */
+               return -119;
+       case 345:
+               /* sendmmsg */
+               return -120;
+       case 359:
+               /* socket */
+               return -101;
+       case 360:
+               /* socketpair */
+               return -108;
+       case 361:
+               /* bind */
+               return -102;
+       case 362:
+               /* connect */
+               return -103;
+       case 363:
+               /* listen */
+               return -104;
+       case 364:
+               /* accept4 */
+               return -118;
+       case 365:
+               /* getsockopt */
+               return -115;
+       case 366:
+               /* setsockopt */
+               return -114;
+       case 367:
+               /* getsockname */
+               return -106;
+       case 368:
+               /* getpeername */
+               return -107;
+       case 369:
+               /* sendto */
+               return -111;
+       case 370:
+               /* sendmsg */
+               return -116;
+       case 371:
+               /* recvfrom */
+               return -112;
+       case 372:
+               /* recvmsg */
+               return -117;
+       case 373:
+               /* shutdown */
+               return -113;
+       }
+
+       return __NR_SCMP_ERROR;
+}
+
+/**
+ * Rewrite a syscall value to match the architecture
+ * @param syscall the syscall number
+ *
+ * Syscalls can vary across different architectures so this function rewrites
+ * the syscall into the correct value for the specified architecture.  Returns
+ * zero on success, negative values on failure.
+ *
+ */
+int x86_syscall_rewrite(int *syscall)
+{
+       int sys = *syscall;
+
+       if (sys <= -100 && sys >= -120)
+               *syscall = __x86_NR_socketcall;
+       else if (sys <= -200 && sys >= -224)
+               *syscall = __x86_NR_ipc;
+       else if (sys < 0)
+               return -EDOM;
+
+       return 0;
+}
+
+/**
+ * add a new rule to the x86 seccomp filter
+ * @param col the filter collection
+ * @param db the seccomp filter db
+ * @param strict the strict flag
+ * @param rule the filter rule
+ *
+ * This function adds a new syscall filter to the seccomp filter db, making any
+ * necessary adjustments for the x86 ABI.  Returns zero on success, negative
+ * values on failure.
+ *
+ */
+int x86_rule_add(struct db_filter_col *col, struct db_filter *db, bool strict,
+                struct db_api_rule_list *rule)
+{
+       int rc;
+       unsigned int iter;
+       size_t args_size;
+       int sys = rule->syscall;
+       int sys_a, sys_b;
+       struct db_api_rule_list *rule_a, *rule_b;
+
+       if ((sys <= -100 && sys >= -120) || (sys >= 359 && sys <= 373)) {
+               /* (-100 to -120) : multiplexed socket syscalls
+                  (359 to 373)   : direct socket syscalls, Linux 4.3+ */
+
+               /* strict check for the multiplexed socket syscalls */
+               for (iter = 0; iter < rule->args_cnt; iter++) {
+                       if ((rule->args[iter].valid != 0) && (strict))
+                               return -EINVAL;
+               }
+
+               /* determine both the muxed and direct syscall numbers */
+               if (sys > 0) {
+                       sys_a = _x86_sock_mux(sys);
+                       if (sys_a == __NR_SCMP_ERROR)
+                               return __NR_SCMP_ERROR;
+                       sys_b = sys;
+               } else {
+                       sys_a = sys;
+                       sys_b = _x86_sock_demux(sys);
+                       if (sys_b == __NR_SCMP_ERROR)
+                               return __NR_SCMP_ERROR;
+               }
+
+               /* use rule_a for the multiplexed syscall and use rule_b for
+                * the direct wired syscall */
+
+               if (sys_a == __NR_SCMP_UNDEF) {
+                       rule_a = NULL;
+                       rule_b = rule;
+               } else if (sys_b == __NR_SCMP_UNDEF) {
+                       rule_a = rule;
+                       rule_b = NULL;
+               } else {
+                       /* need two rules, dup the first and link together */
+                       rule_a = rule;
+                       rule_b = malloc(sizeof(*rule_b));
+                       if (rule_b == NULL)
+                               return -ENOMEM;
+                       args_size = sizeof(*rule_b->args) * rule_a->args_cnt;
+                       rule_b->args = malloc(args_size);
+                       if (rule_b->args == NULL) {
+                               free(rule_b);
+                               return -ENOMEM;
+                       }
+                       rule_b->action = rule_a->action;
+                       rule_b->syscall = rule_a->syscall;
+                       rule_b->args_cnt = rule_a->args_cnt;
+                       memcpy(rule_b->args, rule_a->args, args_size);
+                       rule_b->prev = rule_a;
+                       rule_b->next = NULL;
+                       rule_a->next = rule_b;
+               }
+
+               /* multiplexed socket syscalls */
+               if (rule_a != NULL) {
+                       rule_a->syscall = __x86_NR_socketcall;
+                       rule_a->args[0].arg = 0;
+                       rule_a->args[0].op = SCMP_CMP_EQ;
+                       rule_a->args[0].mask = DATUM_MAX;
+                       rule_a->args[0].datum = (-sys_a) % 100;
+                       rule_a->args[0].valid = 1;
+               }
+
+               /* direct wired socket syscalls */
+               if (rule_b != NULL)
+                       rule_b->syscall = sys_b;
+
+               /* add the rules as a single transaction */
+               rc = db_col_transaction_start(col);
+               if (rc < 0)
+                       return rc;
+               if (rule_a != NULL) {
+                       rc = db_rule_add(db, rule_a);
+                       if (rc < 0)
+                               goto fail_transaction;
+               }
+               if (rule_b != NULL) {
+                       rc = db_rule_add(db, rule_b);
+                       if (rc < 0)
+                               goto fail_transaction;
+               }
+               db_col_transaction_commit(col);
+       } else if (sys <= -200 && sys >= -224) {
+               /* multiplexed ipc syscalls */
+               for (iter = 0; iter < ARG_COUNT_MAX; iter++) {
+                       if ((rule->args[iter].valid != 0) && (strict))
+                               return -EINVAL;
+               }
+               rule->args[0].arg = 0;
+               rule->args[0].op = SCMP_CMP_EQ;
+               rule->args[0].mask = DATUM_MAX;
+               rule->args[0].datum = abs(sys) % 200;
+               rule->args[0].valid = 1;
+               rule->syscall = __x86_NR_ipc;
+
+               rc = db_rule_add(db, rule);
+               if (rc < 0)
+                       return rc;
+       } else if (sys >= 0) {
+               /* normal syscall processing */
+               rc = db_rule_add(db, rule);
+               if (rc < 0)
+                       return rc;
+       } else if (strict)
+               return -EDOM;
+
+       return 0;
+
+fail_transaction:
+       db_col_transaction_abort(col);
+       return rc;
+}
This page took 0.01432 seconds and 4 git commands to generate.