From 47516603828396f85107ea3e2a254958c2bc3ff5 Mon Sep 17 00:00:00 2001
From: Paul Moore <paul@paul-moore.com>
Date: Wed, 20 Apr 2016 13:49:04 -0400
Subject: [PATCH] s390: handle multiplexed syscalls correctly

It turns out the socket and ipc related syscalls are also multiplexed
on s390/s390x much like they are on 32-bit x86.  Further making things
difficult is that starting with Linux 4.3 the socket syscalls are also
available as directly wired versions, much like 32-bit x86.  This
patch attempts to fix all those problems.

Signed-off-by: Paul Moore <paul@paul-moore.com>
(imported from commit 66282c31034e2bb442bd9dc862d6d814f0df2f98)
---
 src/arch-s390-syscalls.c              |  84 +++++++++
 src/arch-s390.c                       | 310 +++++++++++++++++++++++++++++++++-
 src/arch-s390.h                       |  11 +-
 src/arch-s390x-syscalls.c             |  84 +++++++++
 src/arch-s390x.c                      | 310 +++++++++++++++++++++++++++++++++-
 src/arch-s390x.h                      |  13 +-
 src/arch-x86.c                        |   2 +-
 tests/.gitignore                      |   1 +
 tests/33-sim-socket_syscalls_be.c     |  81 +++++++++
 tests/33-sim-socket_syscalls_be.py    |  48 ++++++
 tests/33-sim-socket_syscalls_be.tests |  39 +++++
 tests/Makefile.am                     |   9 +-
 12 files changed, 979 insertions(+), 13 deletions(-)
 create mode 100644 tests/33-sim-socket_syscalls_be.c
 create mode 100755 tests/33-sim-socket_syscalls_be.py
 create mode 100644 tests/33-sim-socket_syscalls_be.tests

diff --git a/src/arch-s390-syscalls.c b/src/arch-s390-syscalls.c
index a6cf826..a04673a 100644
--- a/src/arch-s390-syscalls.c
+++ b/src/arch-s390-syscalls.c
@@ -453,6 +453,48 @@ int s390_syscall_resolve_name(const char *name)
 	const struct arch_syscall_def *table = s390_syscall_table;
 
 	/* XXX - plenty of room for future improvement here */
+
+	if (strcmp(name, "accept") == 0)
+		return __PNR_accept;
+	if (strcmp(name, "accept4") == 0)
+		return __PNR_accept4;
+	else if (strcmp(name, "bind") == 0)
+		return __PNR_bind;
+	else if (strcmp(name, "connect") == 0)
+		return __PNR_connect;
+	else if (strcmp(name, "getpeername") == 0)
+		return __PNR_getpeername;
+	else if (strcmp(name, "getsockname") == 0)
+		return __PNR_getsockname;
+	else if (strcmp(name, "getsockopt") == 0)
+		return __PNR_getsockopt;
+	else if (strcmp(name, "listen") == 0)
+		return __PNR_listen;
+	else if (strcmp(name, "recv") == 0)
+		return __PNR_recv;
+	else if (strcmp(name, "recvfrom") == 0)
+		return __PNR_recvfrom;
+	else if (strcmp(name, "recvmsg") == 0)
+		return __PNR_recvmsg;
+	else if (strcmp(name, "recvmmsg") == 0)
+		return __PNR_recvmmsg;
+	else if (strcmp(name, "send") == 0)
+		return __PNR_send;
+	else if (strcmp(name, "sendmsg") == 0)
+		return __PNR_sendmsg;
+	else if (strcmp(name, "sendmmsg") == 0)
+		return __PNR_sendmmsg;
+	else if (strcmp(name, "sendto") == 0)
+		return __PNR_sendto;
+	else if (strcmp(name, "setsockopt") == 0)
+		return __PNR_setsockopt;
+	else if (strcmp(name, "shutdown") == 0)
+		return __PNR_shutdown;
+	else if (strcmp(name, "socket") == 0)
+		return __PNR_socket;
+	else if (strcmp(name, "socketpair") == 0)
+		return __PNR_socketpair;
+
 	for (iter = 0; table[iter].name != NULL; iter++) {
 		if (strcmp(name, table[iter].name) == 0)
 			return table[iter].num;
@@ -476,6 +518,48 @@ const char *s390_syscall_resolve_num(int num)
 	const struct arch_syscall_def *table = s390_syscall_table;
 
 	/* XXX - plenty of room for future improvement here */
+
+	if (num == __PNR_accept)
+		return "accept";
+	else if (num == __PNR_accept4)
+		return "accept4";
+	else if (num == __PNR_bind)
+		return "bind";
+	else if (num == __PNR_connect)
+		return "connect";
+	else if (num == __PNR_getpeername)
+		return "getpeername";
+	else if (num == __PNR_getsockname)
+		return "getsockname";
+	else if (num == __PNR_getsockopt)
+		return "getsockopt";
+	else if (num == __PNR_listen)
+		return "listen";
+	else if (num == __PNR_recv)
+		return "recv";
+	else if (num == __PNR_recvfrom)
+		return "recvfrom";
+	else if (num == __PNR_recvmsg)
+		return "recvmsg";
+	else if (num == __PNR_recvmmsg)
+		return "recvmmsg";
+	else if (num == __PNR_send)
+		return "send";
+	else if (num == __PNR_sendmsg)
+		return "sendmsg";
+	else if (num == __PNR_sendmmsg)
+		return "sendmmsg";
+	else if (num == __PNR_sendto)
+		return "sendto";
+	else if (num == __PNR_setsockopt)
+		return "setsockopt";
+	else if (num == __PNR_shutdown)
+		return "shutdown";
+	else if (num == __PNR_socket)
+		return "socket";
+	else if (num == __PNR_socketpair)
+		return "socketpair";
+
 	for (iter = 0; table[iter].num != __NR_SCMP_ERROR; iter++) {
 		if (num == table[iter].num)
 			return table[iter].name;
diff --git a/src/arch-s390.c b/src/arch-s390.c
index f509809..fca5fee 100644
--- a/src/arch-s390.c
+++ b/src/arch-s390.c
@@ -5,11 +5,16 @@
 
 #include <stdlib.h>
 #include <errno.h>
+#include <string.h>
 #include <linux/audit.h>
 
 #include "arch.h"
 #include "arch-s390.h"
 
+/* s390 syscall numbers */
+#define __s390_NR_socketcall		102
+#define __s390_NR_ipc			117
+
 const struct arch_def arch_def_s390 = {
 	.token = SCMP_ARCH_S390,
 	.token_bpf = AUDIT_ARCH_S390,
@@ -17,6 +22,307 @@ const struct arch_def arch_def_s390 = {
 	.endian = ARCH_ENDIAN_BIG,
 	.syscall_resolve_name = s390_syscall_resolve_name,
 	.syscall_resolve_num = s390_syscall_resolve_num,
-	.syscall_rewrite = NULL,
-	.rule_add = NULL,
+	.syscall_rewrite = s390_syscall_rewrite,
+	.rule_add = s390_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 _s390_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 _s390_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 s390_syscall_rewrite(int *syscall)
+{
+	int sys = *syscall;
+
+	if (sys <= -100 && sys >= -120)
+		*syscall = __s390_NR_socketcall;
+	else if (sys <= -200 && sys >= -211)
+		*syscall = __s390_NR_ipc;
+	else if (sys < 0)
+		return -EDOM;
+
+	return 0;
+}
+
+/**
+ * add a new rule to the s390 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 s390 ABI.  Returns zero on success, negative
+ * values on failure.
+ *
+ */
+int s390_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 = _s390_sock_mux(sys);
+			if (sys_a == __NR_SCMP_ERROR)
+				return __NR_SCMP_ERROR;
+			sys_b = sys;
+		} else {
+			sys_a = sys;
+			sys_b = _s390_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 = __s390_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 >= -211) {
+		/* 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 = __s390_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;
+}
diff --git a/src/arch-s390.h b/src/arch-s390.h
index 71ba260..899c952 100644
--- a/src/arch-s390.h
+++ b/src/arch-s390.h
@@ -3,12 +3,13 @@
  * Author: Jan Willeke <willeke@linux.vnet.com.com>
  */
 
-#ifndef _ARCH_s390_H
-#define _ARCH_s390_H
+#ifndef _ARCH_S390_H
+#define _ARCH_S390_H
 
 #include <inttypes.h>
 
 #include "arch.h"
+#include "db.h"
 #include "system.h"
 
 #define s390_arg_count_max		6
@@ -18,6 +19,12 @@ extern const struct arch_def arch_def_s390;
 
 int s390_syscall_resolve_name(const char *name);
 const char *s390_syscall_resolve_num(int num);
+
 const char *s390_syscall_iterate_name(unsigned int spot);
 
+int s390_syscall_rewrite(int *syscall);
+
+int s390_rule_add(struct db_filter_col *col, struct db_filter *db, bool strict,
+		  struct db_api_rule_list *rule);
+
 #endif
diff --git a/src/arch-s390x-syscalls.c b/src/arch-s390x-syscalls.c
index 25e4c34..9825c63 100644
--- a/src/arch-s390x-syscalls.c
+++ b/src/arch-s390x-syscalls.c
@@ -453,6 +453,48 @@ int s390x_syscall_resolve_name(const char *name)
 	const struct arch_syscall_def *table = s390x_syscall_table;
 
 	/* XXX - plenty of room for future improvement here */
+
+	if (strcmp(name, "accept") == 0)
+		return __PNR_accept;
+	if (strcmp(name, "accept4") == 0)
+		return __PNR_accept4;
+	else if (strcmp(name, "bind") == 0)
+		return __PNR_bind;
+	else if (strcmp(name, "connect") == 0)
+		return __PNR_connect;
+	else if (strcmp(name, "getpeername") == 0)
+		return __PNR_getpeername;
+	else if (strcmp(name, "getsockname") == 0)
+		return __PNR_getsockname;
+	else if (strcmp(name, "getsockopt") == 0)
+		return __PNR_getsockopt;
+	else if (strcmp(name, "listen") == 0)
+		return __PNR_listen;
+	else if (strcmp(name, "recv") == 0)
+		return __PNR_recv;
+	else if (strcmp(name, "recvfrom") == 0)
+		return __PNR_recvfrom;
+	else if (strcmp(name, "recvmsg") == 0)
+		return __PNR_recvmsg;
+	else if (strcmp(name, "recvmmsg") == 0)
+		return __PNR_recvmmsg;
+	else if (strcmp(name, "send") == 0)
+		return __PNR_send;
+	else if (strcmp(name, "sendmsg") == 0)
+		return __PNR_sendmsg;
+	else if (strcmp(name, "sendmmsg") == 0)
+		return __PNR_sendmmsg;
+	else if (strcmp(name, "sendto") == 0)
+		return __PNR_sendto;
+	else if (strcmp(name, "setsockopt") == 0)
+		return __PNR_setsockopt;
+	else if (strcmp(name, "shutdown") == 0)
+		return __PNR_shutdown;
+	else if (strcmp(name, "socket") == 0)
+		return __PNR_socket;
+	else if (strcmp(name, "socketpair") == 0)
+		return __PNR_socketpair;
+
 	for (iter = 0; table[iter].name != NULL; iter++) {
 		if (strcmp(name, table[iter].name) == 0)
 			return table[iter].num;
@@ -476,6 +518,48 @@ const char *s390x_syscall_resolve_num(int num)
 	const struct arch_syscall_def *table = s390x_syscall_table;
 
 	/* XXX - plenty of room for future improvement here */
+
+	if (num == __PNR_accept)
+		return "accept";
+	else if (num == __PNR_accept4)
+		return "accept4";
+	else if (num == __PNR_bind)
+		return "bind";
+	else if (num == __PNR_connect)
+		return "connect";
+	else if (num == __PNR_getpeername)
+		return "getpeername";
+	else if (num == __PNR_getsockname)
+		return "getsockname";
+	else if (num == __PNR_getsockopt)
+		return "getsockopt";
+	else if (num == __PNR_listen)
+		return "listen";
+	else if (num == __PNR_recv)
+		return "recv";
+	else if (num == __PNR_recvfrom)
+		return "recvfrom";
+	else if (num == __PNR_recvmsg)
+		return "recvmsg";
+	else if (num == __PNR_recvmmsg)
+		return "recvmmsg";
+	else if (num == __PNR_send)
+		return "send";
+	else if (num == __PNR_sendmsg)
+		return "sendmsg";
+	else if (num == __PNR_sendmmsg)
+		return "sendmmsg";
+	else if (num == __PNR_sendto)
+		return "sendto";
+	else if (num == __PNR_setsockopt)
+		return "setsockopt";
+	else if (num == __PNR_shutdown)
+		return "shutdown";
+	else if (num == __PNR_socket)
+		return "socket";
+	else if (num == __PNR_socketpair)
+		return "socketpair";
+
 	for (iter = 0; table[iter].num != __NR_SCMP_ERROR; iter++) {
 		if (num == table[iter].num)
 			return table[iter].name;
diff --git a/src/arch-s390x.c b/src/arch-s390x.c
index 5471738..d14bdf0 100644
--- a/src/arch-s390x.c
+++ b/src/arch-s390x.c
@@ -5,11 +5,16 @@
 
 #include <stdlib.h>
 #include <errno.h>
+#include <string.h>
 #include <linux/audit.h>
 
 #include "arch.h"
 #include "arch-s390x.h"
 
+/* s390x syscall numbers */
+#define __s390x_NR_socketcall		102
+#define __s390x_NR_ipc			117
+
 const struct arch_def arch_def_s390x = {
 	.token = SCMP_ARCH_S390X,
 	.token_bpf = AUDIT_ARCH_S390X,
@@ -17,6 +22,307 @@ const struct arch_def arch_def_s390x = {
 	.endian = ARCH_ENDIAN_BIG,
 	.syscall_resolve_name = s390x_syscall_resolve_name,
 	.syscall_resolve_num = s390x_syscall_resolve_num,
-	.syscall_rewrite = NULL,
-	.rule_add = NULL,
+	.syscall_rewrite = s390x_syscall_rewrite,
+	.rule_add = s390x_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 _s390x_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 _s390x_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 s390x_syscall_rewrite(int *syscall)
+{
+	int sys = *syscall;
+
+	if (sys <= -100 && sys >= -120)
+		*syscall = __s390x_NR_socketcall;
+	else if (sys <= -200 && sys >= -211)
+		*syscall = __s390x_NR_ipc;
+	else if (sys < 0)
+		return -EDOM;
+
+	return 0;
+}
+
+/**
+ * add a new rule to the s390x 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 s390x ABI.  Returns zero on success, negative
+ * values on failure.
+ *
+ */
+int s390x_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 = _s390x_sock_mux(sys);
+			if (sys_a == __NR_SCMP_ERROR)
+				return __NR_SCMP_ERROR;
+			sys_b = sys;
+		} else {
+			sys_a = sys;
+			sys_b = _s390x_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 = __s390x_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 >= -211) {
+		/* 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 = __s390x_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;
+}
diff --git a/src/arch-s390x.h b/src/arch-s390x.h
index 054044b..2857d7f 100644
--- a/src/arch-s390x.h
+++ b/src/arch-s390x.h
@@ -3,12 +3,13 @@
  * Author: Jan Willeke <willeke@linux.vnet.com.com>
  */
 
-#ifndef _ARCH_s390x_H
-#define _ARCH_s390x_H
+#ifndef _ARCH_S390X_H
+#define _ARCH_S390X_H
 
 #include <inttypes.h>
 
 #include "arch.h"
+#include "db.h"
 #include "system.h"
 
 #define s390x_arg_count_max		6
@@ -21,6 +22,12 @@ extern const struct arch_def arch_def_s390x;
 
 int s390x_syscall_resolve_name(const char *name);
 const char *s390x_syscall_resolve_num(int num);
+
 const char *s390x_syscall_iterate_name(unsigned int spot);
-const char *s390x_syscall_iterate_name(unsigned int spot);
+
+int s390x_syscall_rewrite(int *syscall);
+
+int s390x_rule_add(struct db_filter_col *col, struct db_filter *db, bool strict,
+		   struct db_api_rule_list *rule);
+
 #endif
diff --git a/src/arch-x86.c b/src/arch-x86.c
index 1bab53f..1bfe926 100644
--- a/src/arch-x86.c
+++ b/src/arch-x86.c
@@ -232,7 +232,7 @@ int x86_rule_add(struct db_filter_col *col, struct db_filter *db, bool strict,
 
 	if ((sys <= -100 && sys >= -120) || (sys >= 359 && sys <= 373)) {
 		/* (-100 to -120) : multiplexed socket syscalls
-		   (359 to 373)   : direct socket syscalls, Linux 4.4+ */
+		   (359 to 373)   : direct socket syscalls, Linux 4.3+ */
 
 		/* strict check for the multiplexed socket syscalls */
 		for (iter = 0; iter < rule->args_cnt; iter++) {
diff --git a/tests/.gitignore b/tests/.gitignore
index f6c40ee..b282fa4 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -37,3 +37,4 @@ util.pyc
 30-sim-socket_syscalls
 31-basic-version_check
 32-live-tsync_allow
+33-sim-socket_syscalls_be
diff --git a/tests/33-sim-socket_syscalls_be.c b/tests/33-sim-socket_syscalls_be.c
new file mode 100644
index 0000000..7c4d788
--- /dev/null
+++ b/tests/33-sim-socket_syscalls_be.c
@@ -0,0 +1,81 @@
+/**
+ * Seccomp Library test program
+ *
+ * Copyright (c) 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 <errno.h>
+#include <unistd.h>
+
+#include <seccomp.h>
+
+#include "util.h"
+
+int main(int argc, char *argv[])
+{
+	int rc;
+	struct util_options opts;
+	scmp_filter_ctx ctx = NULL;
+
+	rc = util_getopt(argc, argv, &opts);
+	if (rc < 0)
+		goto out;
+
+	ctx = seccomp_init(SCMP_ACT_KILL);
+	if (ctx == NULL)
+		return ENOMEM;
+
+	rc = seccomp_arch_remove(ctx, SCMP_ARCH_NATIVE);
+	if (rc != 0)
+		goto out;
+
+	rc = seccomp_arch_add(ctx, SCMP_ARCH_S390);
+	if (rc != 0)
+		goto out;
+	rc = seccomp_arch_add(ctx, SCMP_ARCH_S390X);
+	if (rc != 0)
+		goto out;
+
+	rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 0);
+	if (rc != 0)
+		goto out;
+
+	rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(connect), 0);
+	if (rc != 0)
+		goto out;
+
+	rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept), 0);
+	if (rc != 0)
+		goto out;
+
+	rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), 0);
+	if (rc != 0)
+		goto out;
+
+	rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(shutdown), 0);
+	if (rc != 0)
+		goto out;
+
+	rc = util_filter_output(&opts, ctx);
+	if (rc)
+		goto out;
+
+out:
+	seccomp_release(ctx);
+	return (rc < 0 ? -rc : rc);
+}
diff --git a/tests/33-sim-socket_syscalls_be.py b/tests/33-sim-socket_syscalls_be.py
new file mode 100755
index 0000000..416fb33
--- /dev/null
+++ b/tests/33-sim-socket_syscalls_be.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+
+#
+# Seccomp Library test program
+#
+# Copyright (c) 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>.
+#
+
+import argparse
+import sys
+
+import util
+
+from seccomp import *
+
+def test(args):
+    f = SyscallFilter(KILL)
+    f.remove_arch(Arch())
+    f.add_arch(Arch("s390"))
+    f.add_arch(Arch("s390x"))
+    f.add_rule(ALLOW, "socket")
+    f.add_rule(ALLOW, "connect")
+    f.add_rule(ALLOW, "accept")
+    f.add_rule(ALLOW, "accept4")
+    f.add_rule(ALLOW, "shutdown")
+    return f
+
+args = util.get_opt()
+ctx = test(args)
+util.filter_output(args, ctx)
+
+# kate: syntax python;
+# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off;
diff --git a/tests/33-sim-socket_syscalls_be.tests b/tests/33-sim-socket_syscalls_be.tests
new file mode 100644
index 0000000..b2523af
--- /dev/null
+++ b/tests/33-sim-socket_syscalls_be.tests
@@ -0,0 +1,39 @@
+#
+# libseccomp regression test automation data
+#
+# Copyright (c) 2016 Red Hat <pmoore@redhat.com>
+# Author: Paul Moore <paul@paul-moore.com>
+#
+
+test type: bpf-sim
+
+# Testname			Arch	Syscall		Arg0		Arg1		Arg2	Arg3	Arg4	Arg5	Result
+33-sim-socket_syscalls_be	+s390	socketcall	1		N		N	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390	socketcall	3		N		N	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390	socketcall	5		N		N	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390	socketcall	13		N		N	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390	359		0		1		2	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390	362		0		1		2	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390	364		0		1		2	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390	373		0		1		2	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390	accept		5		N		N	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390	accept		0		1		2	N	N	N	KILL
+33-sim-socket_syscalls_be	+s390	accept4		18		1		2	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390	accept4		0		1		2	N	N	N	KILL
+33-sim-socket_syscalls_be	+s390x	socketcall	1		N		N	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390x	socketcall	3		N		N	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390x	socketcall	5		N		N	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390x	socketcall	13		N		N	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390x	359		0		1		2	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390x	362		0		1		2	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390x	364		0		1		2	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390x	373		0		1		2	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390x	accept		5		N		N	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390x	accept		0		1		2	N	N	N	KILL
+33-sim-socket_syscalls_be	+s390x	accept4		18		1		2	N	N	N	ALLOW
+33-sim-socket_syscalls_be	+s390x	accept4		0		1		2	N	N	N	KILL
+
+test type: bpf-valgrind
+
+# Testname
+33-sim-socket_syscalls_be
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5f17e97..ee4e41b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -60,7 +60,8 @@ check_PROGRAMS = \
 	29-sim-pseudo_syscall \
 	30-sim-socket_syscalls \
 	31-basic-version_check \
-	32-live-tsync_allow
+	32-live-tsync_allow \
+	33-sim-socket_syscalls_be
 
 EXTRA_DIST_TESTPYTHON = \
 	util.py \
@@ -95,7 +96,8 @@ EXTRA_DIST_TESTPYTHON = \
 	29-sim-pseudo_syscall.py \
 	30-sim-socket_syscalls.py \
 	31-basic-version_check.py \
-	32-live-tsync_allow.py
+	32-live-tsync_allow.py \
+	33-sim-socket_syscalls_be.py
 
 EXTRA_DIST_TESTCFGS = \
 	01-sim-allow.tests \
@@ -129,7 +131,8 @@ EXTRA_DIST_TESTCFGS = \
 	29-sim-pseudo_syscall.tests \
 	30-sim-socket_syscalls.tests \
 	31-basic-version_check.tests \
-	32-live-tsync_allow.tests
+	32-live-tsync_allow.tests \
+	33-sim-socket_syscalls_be.tests
 
 EXTRA_DIST_TESTSCRIPTS = regression testdiff testgen
 
-- 
2.8.4

