[lxc-devel] [lxc/master] Fix issue #1564, add seccomp limit syscall argument

lifeng68 on Github lxc-bot at linuxcontainers.org
Thu Nov 23 10:58:22 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 1421 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20171123/2f280421/attachment.bin>
-------------- next part --------------
From cbea56a0bb5e38397168c66ea1ff86da1c8885bf Mon Sep 17 00:00:00 2001
From: LiFeng <lifeng68 at huawei.com>
Date: Thu, 23 Nov 2017 14:15:23 -0500
Subject: [PATCH] Fix issue #1564,add seccomp limit syscall argument
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

To support limit syscall arguments, extend the version 2 file to the following format:

```
syscall_name default_action args:num [index,value,valueTwo,op]...
```

num: the number of the, the maximum of num is 6。
for one arguments, [index,value,valueTwo,op]

index: the index for syscall arguments(type uint, unsigned decimal integer)
value: the value for syscall arguments (type uint64, unsigned decimal integer)
valueTwo: the value for syscall arguments (type uint64, unsigned decimal integer)
op: the operator for syscall arguments(string), a valid list of constants as of
libseccomp v2.3.2 is
SCMP_CMP_NE,SCMP_CMP_LE,SCMP_CMP_LE, SCMP_CMP_EQ, SCMP_CMP_GE,
SCMP_CMP_GT, SCMP_CMP_MASKED_EQ.
For example:

```
2
blacklist
reject_force_umount  # comment this to allow umount -f;  not recommended
[all]
kexec_load errno 1 args:3 [0,1,0,SCMP_CMP_LE][3,1,0,SCMP_CMP_GT][5,1,0,SCMP_CMP_MASKED_EQ]
open_by_handle_at errno 1
init_module errno 1
finit_module errno 1
delete_module errno 1
```
Signed-off-by: LiFeng <lifeng68 at huawei.com>
---
 src/lxc/seccomp.c | 214 ++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 191 insertions(+), 23 deletions(-)

diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c
index deacd1217..c7387e804 100644
--- a/src/lxc/seccomp.c
+++ b/src/lxc/seccomp.c
@@ -109,14 +109,13 @@ static const char *get_action_name(uint32_t action)
 	}
 }
 
-static uint32_t get_and_clear_v2_action(char *line, uint32_t def_action)
+static uint32_t get_v2_action(char *line, uint32_t def_action)
 {
 	char *p = strchr(line, ' ');
 	uint32_t ret;
 
 	if (!p)
 		return def_action;
-	*p = '\0';
 	p++;
 	while (*p == ' ')
 		p++;
@@ -129,6 +128,151 @@ static uint32_t get_and_clear_v2_action(char *line, uint32_t def_action)
 	default: return ret;
 	}
 }
+
+struct v2_rule_args {
+	uint32_t index;
+	uint64_t value;
+	uint64_t mask;
+	enum scmp_compare op;
+};
+
+struct seccomp_v2_rule {
+	uint32_t action;
+	uint32_t args_num;
+	struct v2_rule_args args_value[6];
+};
+
+static enum scmp_compare parse_v2_rule_op(char *s)
+{
+	enum scmp_compare ret;
+
+	if (strcmp(s, "SCMP_CMP_NE") == 0)
+		ret = SCMP_CMP_NE;
+	else if (strcmp(s, "SCMP_CMP_LT") == 0)
+		ret = SCMP_CMP_LT;
+	else if (strcmp(s, "SCMP_CMP_LE") == 0)
+		ret = SCMP_CMP_LE;
+	else if (strcmp(s, "SCMP_CMP_EQ") == 0)
+		ret = SCMP_CMP_EQ;
+	else if (strcmp(s, "SCMP_CMP_GE") == 0)
+		ret = SCMP_CMP_GE;
+	else if (strcmp(s, "SCMP_CMP_GT") == 0)
+		ret = SCMP_CMP_GT;
+	else if (strcmp(s, "SCMP_CMP_MASKED_EQ") == 0)
+		ret = SCMP_CMP_MASKED_EQ;
+	else
+		ret = -1;
+
+	return ret;
+}
+
+static uint32_t get_seccomp_arg_num(char *line)
+{
+	uint32_t num = -1;
+	char *tmp = NULL;
+	int ret = 0;
+
+	/*read optional args which follows the syscall*/
+	tmp = strstr(line, "args:");
+	if (!tmp) {
+		INFO("Seccomp rule include none args");
+		return 0;
+	}
+
+	ret = sscanf(tmp, "args:%u", &num);
+	if (ret != 1 || num > 6) {
+		INFO("Failed to interpret valid args number");
+		return -1;
+	}
+
+	return num;
+}
+
+static int get_seccomp_arg_value(char *key, struct v2_rule_args *rule_args)
+{
+	int ret = 0;
+	uint64_t value = 0;
+	uint64_t mask = 0;
+	enum scmp_compare op = 0;
+	uint32_t index = 0;
+	char s[30] = {0};
+	char *tmp = NULL;
+
+	memset(s, 0x00, sizeof(s));
+	tmp = strchr(key, '[');
+	if (!tmp) {
+		ERROR("Failed to interpret args.");
+		return -1;
+	}
+	ret = sscanf(tmp, "[%u,%llu,%llu,%30s", &index, (long long unsigned int *)&value, (long long unsigned int *)&mask, s);
+	if (ret != 4 || index >= 6) {
+		ERROR("Failed to interpret args value.");
+		return -1;
+	}
+
+	op = parse_v2_rule_op(s);
+	if (op == -1) {
+		ERROR("Failed to interpret args operator value.");
+		return -1;
+	}
+
+	rule_args->index = index;
+	rule_args->value = value;
+	rule_args->mask = mask;
+	rule_args->op = op;
+	return 0;
+}
+
+static int parse_v2_rules(char *line, uint32_t def_action, struct seccomp_v2_rule *rules)
+{
+	int ret = 0 ;
+	int i = 0;
+	char *tmp = NULL;
+	char *key = NULL;
+	char *saveptr = NULL;
+
+	tmp = strdup(line);
+	if (!tmp)
+		return -1;
+
+	/* read optional action which follows the syscall */
+	rules->action = get_v2_action(tmp, def_action);
+	if (rules->action == -1) {
+		ERROR("Failed to interpret action.");
+		ret = -1;
+		goto out;
+	}
+
+	rules->args_num = get_seccomp_arg_num(tmp);
+	if (rules->args_num == -1) {
+		ret = -1;
+		goto out;
+	}
+	if (rules->args_num == 0) {
+		ret = 0;
+		goto out;
+	}
+
+	for ((key = strtok_r(tmp, "]", &saveptr)), i = 0; key && i < rules->args_num; (key = strtok_r(NULL, "]", &saveptr)), i++) {
+		ret = get_seccomp_arg_value(key, &rules->args_value[i]);
+		if (ret) {
+			ret = -1;
+			goto out;
+		}
+	}
+	if (i != rules->args_num) {
+		ERROR("Failed to interpret sufficient parameters.");
+		ret = -1;
+		goto out;
+	}
+
+	ret = 0;
+out:
+	if (tmp)
+		free(tmp);
+	return ret;
+}
+
 #endif
 
 #if HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH
@@ -260,9 +404,12 @@ scmp_filter_ctx get_new_ctx(enum lxc_hostarch_t n_arch, uint32_t default_policy_
 }
 
 bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx,
-			uint32_t action)
+			struct seccomp_v2_rule *rule)
 {
-	int nr, ret;
+	int nr, ret, i;
+	struct scmp_arg_cmp arg_cmp[6];
+
+	memset(arg_cmp, 0x00 ,sizeof(arg_cmp));
 
 	ret = seccomp_arch_exist(ctx, arch);
 	if (arch && ret != 0) {
@@ -272,6 +419,11 @@ bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx,
 		return false;
 	}
 
+	/*get the syscall name*/
+	char *p = strchr(line, ' ');
+	if (p)
+		*p = '\0';
+
 	if (strncmp(line, "reject_force_umount", 19) == 0) {
 		INFO("Setting Seccomp rule to reject force umounts.");
 		ret = seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(umount2),
@@ -296,10 +448,24 @@ bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx,
 		WARN("This syscall will NOT be blacklisted.");
 		return true;
 	}
-	ret = seccomp_rule_add_exact(ctx, action, nr, 0);
+
+	for (i = 0; i < rule->args_num; i++) {
+		INFO("arg_cmp[%d]:SCMP_CMP(%u, %llu, %llu, %llu)", i,
+		      rule->args_value[i].index,
+		      (long long unsigned int)rule->args_value[i].op,
+		      (long long unsigned int)rule->args_value[i].mask,
+		      (long long unsigned int)rule->args_value[i].value);
+
+		if (SCMP_CMP_MASKED_EQ == rule->args_value[i].op)
+			arg_cmp[i] = SCMP_CMP(rule->args_value[i].index, rule->args_value[i].op, rule->args_value[i].mask, rule->args_value[i].value);
+		else
+			arg_cmp[i] = SCMP_CMP(rule->args_value[i].index, rule->args_value[i].op, rule->args_value[i].value);
+	}
+
+	ret = seccomp_rule_add_exact_array(ctx, rule->action, nr, rule->args_num, arg_cmp);
 	if (ret < 0) {
 		ERROR("Failed (%d) loading rule for %s (nr %d action %d(%s)): %s.",
-		      ret, line, nr, action, get_action_name(action), strerror(-ret));
+		      ret, line, nr, rule->action, get_action_name(rule->action), strerror(-ret));
 		return false;
 	}
 	return true;
@@ -325,10 +491,11 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
 	int ret;
 	scmp_filter_ctx compat_ctx[2] = {NULL, NULL};
 	bool blacklist = false;
-	uint32_t default_policy_action = -1, default_rule_action = -1, action;
+	uint32_t default_policy_action = -1, default_rule_action = -1;
 	enum lxc_hostarch_t native_arch = get_hostarch(),
 			    cur_rule_arch = native_arch;
 	uint32_t compat_arch[2] = {SCMP_ARCH_NATIVE, SCMP_ARCH_NATIVE};
+	struct seccomp_v2_rule rule;
 
 	if (strncmp(line, "blacklist", 9) == 0)
 		blacklist = true;
@@ -580,19 +747,20 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
 		if (cur_rule_arch == lxc_seccomp_arch_unknown)
 			continue;
 
+		memset(&rule, 0x00, sizeof(rule));
 		/* read optional action which follows the syscall */
-		action = get_and_clear_v2_action(line, default_rule_action);
-		if (action == -1) {
-			ERROR("Failed to interpret action.");
+		ret = parse_v2_rules(line, default_rule_action, &rule);
+		if (ret != 0) {
+			ERROR("Failed to interpret seccomp rule.");
 			goto bad_rule;
 		}
 
 		if (cur_rule_arch == native_arch ||
 		    cur_rule_arch == lxc_seccomp_arch_native ||
 		    compat_arch[0] == SCMP_ARCH_NATIVE) {
-			INFO("Adding native rule for %s action %d(%s).", line, action,
-			     get_action_name(action));
-			if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action))
+			INFO("Adding native rule for %s action %d(%s).", line, rule.action,
+			     get_action_name(rule.action));
+			if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, &rule))
 				goto bad_rule;
 		}
 		else if (cur_rule_arch != lxc_seccomp_arch_all) {
@@ -600,22 +768,22 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
 				cur_rule_arch == lxc_seccomp_arch_mips64n32 ||
 				cur_rule_arch == lxc_seccomp_arch_mipsel64n32 ? 1 : 0;
 
-			INFO("Adding compat-only rule for %s action %d(%s).", line, action,
-			     get_action_name(action));
-			if (!do_resolve_add_rule(compat_arch[arch_index], line, compat_ctx[arch_index], action))
+			INFO("Adding compat-only rule for %s action %d(%s).", line, rule.action,
+			     get_action_name(rule.action));
+			if (!do_resolve_add_rule(compat_arch[arch_index], line, compat_ctx[arch_index], &rule))
 				goto bad_rule;
 		}
 		else {
-			INFO("Adding native rule for %s action %d(%s).", line, action,
-			     get_action_name(action));
-			if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action))
+			INFO("Adding native rule for %s action %d(%s).", line, rule.action,
+			     get_action_name(rule.action));
+			if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, &rule))
 				goto bad_rule;
-			INFO("Adding compat rule for %s action %d(%s).", line, action,
-			     get_action_name(action));
-			if (!do_resolve_add_rule(compat_arch[0], line, compat_ctx[0], action))
+			INFO("Adding compat rule for %s action %d(%s).", line, rule.action,
+			     get_action_name(rule.action));
+			if (!do_resolve_add_rule(compat_arch[0], line, compat_ctx[0], &rule))
 				goto bad_rule;
 			if (compat_arch[1] != SCMP_ARCH_NATIVE &&
-				!do_resolve_add_rule(compat_arch[1], line, compat_ctx[1], action))
+				!do_resolve_add_rule(compat_arch[1], line, compat_ctx[1], &rule))
 				goto bad_rule;
 		}
 	}


More information about the lxc-devel mailing list