From 876a52e95a9d24a4b9fa37325d83bcb7d3c7b160 Mon Sep 17 00:00:00 2001 From: Alice Frosi Date: Fri, 30 Jun 2023 11:22:52 +0200 Subject: test: fix filter-build test and bugs The tests for checking the filter build recompile and are successfull. Changes: - spotted a couple of bugs for adding the arguments in the filter - readded function `filter_flush_args` to flush_args; this is needed to distinguish when the arguments belong to the same block or are different entries to the same syscall - build the filter in a way that CMP_EQ corresponds to BPF_JEQ and we don't need to awkwardly negate the operations (still TODO for AND_EQ) --- common/gluten.h | 1 - cooker/emit.c | 2 - cooker/filter.c | 76 ++++++++++---------------- cooker/filter.h | 2 +- cooker/match.c | 1 + tests/unit/Makefile | 23 ++++---- tests/unit/test_filter_build.c | 121 +++++++++++++++++++++-------------------- tests/unit/testutil.h | 4 +- tests/unit/util.c | 19 ++++++- 9 files changed, 125 insertions(+), 124 deletions(-) diff --git a/common/gluten.h b/common/gluten.h index f3ef47d..83cfbc3 100644 --- a/common/gluten.h +++ b/common/gluten.h @@ -34,7 +34,6 @@ extern struct seccomp_data anonymous_seccomp_data; #define OP_EMPTY { .block = { -1 } } #define NO_FIELD block -#define NS_NUM sizeof(enum ns_type) #define GET_BIT(x, i) (((x) & (1UL << (i))) != 0) enum gluten_offset_type { diff --git a/cooker/emit.c b/cooker/emit.c index ee52b17..28afd29 100644 --- a/cooker/emit.c +++ b/cooker/emit.c @@ -559,8 +559,6 @@ void emit_bpf_arg(int index, enum type type, union value v, union value mask, */ if (mask.v_num) bpf.cmp = (cmp == CMP_EQ) ? AND_NE : AND_EQ; - else - bpf.cmp = (cmp == CMP_EQ) ? NE : EQ; bpf.arg = index; diff --git a/cooker/filter.c b/cooker/filter.c index c7e0ee4..4a06111 100644 --- a/cooker/filter.c +++ b/cooker/filter.c @@ -13,7 +13,7 @@ #include #include -#include +#include "util.h" #include "filter.h" @@ -37,29 +37,12 @@ struct bpf_entry { struct filter_call_input { bool notify; bool ignore_args; + unsigned int count; struct bpf_entry entry[MAX_ENTRIES_PER_SYSCALL]; } filter_input[N_SYSCALL] = { 0 }; static long current_nr; -/** - * entry_has_check() - Input stage: does the syscall entry need argument checks? - * @entry: syscall entry with field checks - * - * Return: true if at least one argument comparison is requested - */ -static bool entry_has_check(const struct bpf_entry *entry) -{ - unsigned i; - - for (i = 0; i < MAX_FIELDS_PER_SYSCALL; i++) { - if (entry->field[i].cmp != NO_CHECK) - return true; - } - - return false; -} - /** * call_entry_count() - Input stage: count of entries for the same syscall * @nr: syscall number @@ -69,19 +52,8 @@ static bool entry_has_check(const struct bpf_entry *entry) static unsigned int call_entry_count(long nr) { struct filter_call_input *call = filter_input + nr; - unsigned i, count = 0; - - if (!call->notify || call->ignore_args) - return 0; - - for (i = 0; i < MAX_ENTRIES_PER_SYSCALL; i++) { - if (entry_has_check(&call->entry[i])) - count++; - else - break; - } - return count; + return call->count; } /** @@ -170,6 +142,17 @@ void filter_needs_deref(void) call->ignore_args = true; } +void filter_flush_args(long nr) +{ + struct filter_call_input *call = filter_input + nr; + struct bpf_entry *entry = &call->entry[(call->count)]; + + if (entry_check_count(entry) > 0) + call->count++; + if (call->count > MAX_FIELDS_PER_SYSCALL) + call->ignore_args = true; +} + /* Calculate how many instruction for the syscall */ static unsigned int get_n_args_syscall_instr(long nr) { @@ -469,6 +452,9 @@ static unsigned int insert_args(struct sock_filter filter[], long nr) unsigned int size = 0; unsigned int i, j; + /* No entries, hence no arguments for the @nr syscall */ + if (count <= 0) + return 0; for (i = 0; i < count; i++) { n_checks = 0; entry = &call->entry[i]; @@ -478,10 +464,14 @@ static unsigned int insert_args(struct sock_filter filter[], long nr) * jump to the next group if the check is false. */ next_offset = entry_check_count(entry); - for (j = 0; j < entry_check_count(entry); j++) { + for (j = 0; j <= entry_check_count(entry); j++) { struct bpf_field *field = &entry->field[j]; - + /* If the current argument isn't the last argument (offset = 1), + * add an additional jump as there is seccomp notify instruction + * before the allow one. + */ offset = next_offset - n_checks; + offset += (offset > 1) ? 1 : 0; switch (field->cmp) { case NO_CHECK: continue; @@ -512,12 +502,10 @@ static unsigned int insert_args(struct sock_filter filter[], long nr) } n_checks++; } - /* If there were no arguments for this entry, then we don't need - * to add the notification */ if (n_checks > 0) - filter[size++] = STMT(BPF_RET | BPF_K, - SECCOMP_RET_ALLOW); + filter[size++] = STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF); } + filter[size++] = STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW); return size; } @@ -571,7 +559,7 @@ unsigned int filter_build(struct sock_filter filter[], unsigned n) for (i = 0; i < n; i++) { nr = get_syscall(i); if (call_entry_count(nr) > 0) { - offset = next_offset - 1; + offset = next_offset; } else { /* If the syscall doesn't have any arguments, then notify */ offset = notify - size - 1; @@ -584,21 +572,15 @@ unsigned int filter_build(struct sock_filter filter[], unsigned n) } /* Seccomp accept and notify instruction */ filter[size++] = STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW); - if (!call_entry_count(nr)) - filter[size++] = STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF); - + filter[size++] = STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF); /* * Insert args. It sequentially checks all the arguments for a syscall * entry. If a check on the argument isn't equal then it jumps to * check the following entry of the syscall and its arguments. */ - for (i = 0; i < n; i++) { - size += insert_args(&filter[size], nr); - if (call_entry_count(nr)) - filter[size++] = STMT(BPF_RET | BPF_K, - SECCOMP_RET_USER_NOTIF); - } + for (i = 0; i < n; i++) + size += insert_args(&filter[size], get_syscall(i)); debug(" BPF: filter with %i call%s has %i instructions", n, n != 1 ? "s" : "", size); diff --git a/cooker/filter.h b/cooker/filter.h index 8efdc47..c152b2e 100644 --- a/cooker/filter.h +++ b/cooker/filter.h @@ -70,5 +70,5 @@ void filter_notify(long nr); void filter_needs_deref(void); void filter_add_check(struct bpf_field *field); void filter_write(const char *path); - +void filter_flush_args(long nr); #endif diff --git a/cooker/match.c b/cooker/match.c index c1f5fa8..c83f9fe 100644 --- a/cooker/match.c +++ b/cooker/match.c @@ -381,6 +381,7 @@ void handle_matches(struct gluten_ctx *g, JSON_Value *value) filter_notify(call->number); parse_match(g, args, call->args); + filter_flush_args(call->number); break; } diff --git a/tests/unit/Makefile b/tests/unit/Makefile index 1a7f70a..76f815e 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -4,29 +4,30 @@ MAIN_DIR := ../../ COMMON_DIR := ../../common -OP_DIR := ../../ COOKER_DIR := ../../cooker DBG_DIR := ../../debug -SRCS_FILTER_BUILD := $(COOKER_DIR)/filter.c $(DBG_DIR)/disasm.c $(COMMON_DIR)/common.c -HEADERS_FILTER_BUILD := $(COOKER_DIR)/filter.h $(DBG_DIR)/disasm.h $(COMMON_DIR)/common.h +SRCS_FILTER_BUILD := $(COOKER_DIR)/filter.c $(DBG_DIR)/disasm.c $(COMMON_DIR)/common.c \ + $(COMMON_DIR)/util.c +HEADERS_FILTER_BUILD := $(COOKER_DIR)/filter.h $(DBG_DIR)/disasm.h $(COMMON_DIR)/common.h \ + $(COMMON_DIR)/util.h SRCS_FILTER := $(COOKER_DIR)/filter.c $(COMMON_DIR)/common.c util.c \ $(DBG_DIR)/disasm.c -HEADERS_FILTER := $(COOKER_DIR)/filter.h $(COMMON_DIR)/common.h \ +HEADERS_FILTER := $(COOKER_DIR)/filter.h $(COMMON_DIR)/common.h $(COMMON_DIR)/util.h \ $(DBG_DIR)/disasm.h testutil.h -HEADERS_OP_CALL := testutil.h $(COMMON_DIR)/gluten.h $(OP_DIR)/operations.h \ +HEADERS_OP_CALL := testutil.h $(COMMON_DIR)/gluten.h $(MAIN_DIR)/operations.h \ $(COMMON_DIR)/common.h $(COMMON_DIR)/util.h -SRCS_OP_CALL := $(COMMON_DIR)/common.c $(OP_DIR)/operations.c util.c $(COMMON_DIR)/util.c +SRCS_OP_CALL := $(COMMON_DIR)/common.c $(MAIN_DIR)/operations.c util.c $(COMMON_DIR)/util.c -HEADERS_OP := $(COMMON_DIR)/gluten.h $(OP_DIR)/operations.h \ +HEADERS_OP := $(COMMON_DIR)/gluten.h $(MAIN_DIR)/operations.h \ $(COMMON_DIR)/common.h testutil.h $(COMMON_DIR)/util.h -SRCS_OP := $(COMMON_DIR)/common.c $(OP_DIR)/operations.c util.c $(COMMON_DIR)/util.c +SRCS_OP := $(COMMON_DIR)/common.c $(MAIN_DIR)/operations.c util.c $(COMMON_DIR)/util.c -HEADERS_ERROR := $(COMMON_DIR)/gluten.h $(OP_DIR)/operations.h \ +HEADERS_ERROR := $(COMMON_DIR)/gluten.h $(MAIN_DIR)/operations.h \ $(COMMON_DIR)/common.h testutil.h $(COMMON_DIR)/util.h -SRCS_ERROR := $(COMMON_DIR)/common.c $(OP_DIR)/operations.c util.c $(COMMON_DIR)/util.c +SRCS_ERROR := $(COMMON_DIR)/common.c $(MAIN_DIR)/operations.c util.c $(COMMON_DIR)/util.c TARGET := $(shell $(CC) -dumpmachine) TARGET_ARCH := $(shell echo $(TARGET) | cut -f1 -d- | tr [A-Z] [a-z]) @@ -38,7 +39,7 @@ AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/PPC64/PPC/') AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/PPCLE/PPC64LE/') CFLAGS += -Wall -Wextra -pedantic -CFLAGS += -I$(MAIN_DIR) -I$(OP_DIR) -I$(DBG_DIR) +CFLAGS += -I$(MAIN_DIR) -I$(DBG_DIR) -I$(COMMON_DIR) -I$(COOKER_DIR) CFLAGS += -lcheck CFLAGS += -DSEITAN_AUDIT_ARCH=AUDIT_ARCH_$(AUDIT_ARCH) diff --git a/tests/unit/test_filter_build.c b/tests/unit/test_filter_build.c index f6d9ba5..52c1a82 100644 --- a/tests/unit/test_filter_build.c +++ b/tests/unit/test_filter_build.c @@ -35,7 +35,7 @@ static bool filter_eq(struct sock_filter *f1, struct sock_filter *f2, START_TEST(test_single_instr) { - struct sock_filter filter[10]; + struct sock_filter result[10]; unsigned int size; long nr = 42; struct sock_filter expected[] = { @@ -53,11 +53,13 @@ START_TEST(test_single_instr) }; filter_notify(nr); + filter_flush_args(nr); filter_write(tfilter); - size = read_filter(filter, tfilter); - ck_assert_uint_eq(size, ARRAY_SIZE(expected)); - ck_assert(filter_eq(expected, filter, ARRAY_SIZE(expected))); + size = read_filter(result, tfilter); + + bpf_disasm_all(result, size); + ck_assert(filter_eq(expected, result, ARRAY_SIZE(expected))); } END_TEST @@ -65,12 +67,12 @@ START_TEST(test_single_instr_two_args) { unsigned int size; long nr = 42; - struct bpf_arg a1 = { .cmp = EQ, - .value = { .v32 = 0x123 }, - .type = BPF_U32 }; - struct bpf_arg a2 = { .cmp = EQ, - .value = { .v32 = 0x321 }, - .type = BPF_U32 }; + struct bpf_field a1 = { + .arg = 1, .cmp = EQ, .value = { .v32 = 0x123 }, .type = BPF_U32 + }; + struct bpf_field a2 = { + .arg = 2, .cmp = EQ, .value = { .v32 = 0x321 }, .type = BPF_U32 + }; struct sock_filter result[20]; struct sock_filter expected[] = { /* l0 */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, @@ -85,7 +87,7 @@ START_TEST(test_single_instr_two_args) /* l5 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /* l6 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), /* l7 */ LOAD(offsetof(struct seccomp_data, args[1])), - /* l8 */ EQ(0x123, 0, 2), + /* l8 */ EQ(0x123, 0, 3), /* l9 */ LOAD(offsetof(struct seccomp_data, args[2])), /* l10 */ EQ(0x321, 0, 1), /* l11 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), @@ -93,14 +95,14 @@ START_TEST(test_single_instr_two_args) }; filter_notify(nr); - filter_add_arg(1, a1); - filter_add_arg(2, a2); - filter_flush_args(); + filter_add_check(&a1); + filter_add_check(&a2); + filter_flush_args(nr); filter_write(tfilter); size = read_filter(result, tfilter); - ck_assert_uint_eq(size, ARRAY_SIZE(expected)); + bpf_disasm_all(result, size); ck_assert(filter_eq(expected, result, ARRAY_SIZE(expected))); } END_TEST @@ -127,18 +129,21 @@ START_TEST(test_two_instr) }; struct sock_filter result[30]; filter_notify(42); + filter_flush_args(42); filter_notify(49); + filter_flush_args(49); filter_write(tfilter); size = read_filter(result, tfilter); - ck_assert_uint_eq(size, ARRAY_SIZE(expected)); + bpf_disasm_all(result, size); ck_assert(filter_eq(expected, result, ARRAY_SIZE(expected))); } END_TEST START_TEST(test_multiple_instr_no_args) { + unsigned long nrs[] = { 42, 43, 44, 45, 46 }; unsigned int size; struct sock_filter expected[] = { /* l0 */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, @@ -170,16 +175,14 @@ START_TEST(test_multiple_instr_no_args) }; struct sock_filter result[sizeof(expected) / sizeof(expected[0]) + 10]; - filter_notify(42); - filter_notify(43); - filter_notify(44); - filter_notify(45); - filter_notify(46); - + for(unsigned int i = 0; i < ARRAY_SIZE(nrs); i++) { + filter_notify(nrs[i]); + filter_flush_args(nrs[i]); + } filter_write(tfilter); size = read_filter(result, tfilter); - ck_assert_uint_eq(size, ARRAY_SIZE(expected)); + bpf_disasm_all(result, size); ck_assert(filter_eq(expected, result, ARRAY_SIZE(expected))); } END_TEST @@ -187,12 +190,12 @@ END_TEST START_TEST(test_multiple_instr_with_args) { unsigned int size; - struct bpf_arg a1 = { .cmp = EQ, - .value = { .v32 = 0x123 }, - .type = BPF_U32 }; - struct bpf_arg a2 = { .cmp = EQ, - .value = { .v32 = 0x321 }, - .type = BPF_U32 }; + struct bpf_field a1 = { + .arg = 1, .cmp = EQ, .value = { .v32 = 0x123 }, .type = BPF_U32 + }; + struct bpf_field a2 = { + .arg = 2, .cmp = EQ, .value = { .v32 = 0x321 }, .type = BPF_U32 + }; struct sock_filter expected[] = { /* l0 */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, arch))), @@ -222,14 +225,14 @@ START_TEST(test_multiple_instr_with_args) /* l17 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), /* ------- args ---------- */ /* l18 */ LOAD(offsetof(struct seccomp_data, args[1])), - /* l19 */ EQ(0x123, 0, 2), + /* l19 */ EQ(0x123, 0, 3), /* l20 */ LOAD(offsetof(struct seccomp_data, args[2])), /* l21 */ EQ(0x321, 0, 1), /* l22 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), /* l23 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /* ----- end call42 ------ */ /* l24 */ LOAD(offsetof(struct seccomp_data, args[1])), - /* l25 */ EQ(0x123, 0, 2), + /* l25 */ EQ(0x123, 0, 3), /* l26 */ LOAD(offsetof(struct seccomp_data, args[2])), /* l27 */ EQ(0x321, 0, 1), /* l28 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), @@ -238,21 +241,21 @@ START_TEST(test_multiple_instr_with_args) }; struct sock_filter result[sizeof(expected) / sizeof(expected[0]) + 10]; filter_notify(42); - filter_add_arg(1, a1); - filter_add_arg(2, a2); - filter_flush_args(); + filter_add_check(&a1); + filter_add_check(&a2); + filter_flush_args(42); filter_notify(43); filter_notify(44); filter_notify(45); - filter_add_arg(1, a1); - filter_add_arg(2, a2); - filter_flush_args(); + filter_add_check(&a1); + filter_add_check(&a2); + filter_flush_args(45); filter_notify(46); filter_write(tfilter); size = read_filter(result, tfilter); - ck_assert_uint_eq(size, ARRAY_SIZE(expected)); + bpf_disasm_all(result, size); ck_assert(filter_eq(expected, result, ARRAY_SIZE(expected))); } END_TEST @@ -260,12 +263,12 @@ END_TEST START_TEST(test_multiple_instance_same_instr) { unsigned int size; - struct bpf_arg a1 = { .cmp = EQ, - .value = { .v32 = 0x123 }, - .type = BPF_U32 }; - struct bpf_arg a2 = { .cmp = EQ, - .value = { .v32 = 0x321 }, - .type = BPF_U32 }; + struct bpf_field a1 = { + .arg = 1, .cmp = EQ, .value = { .v32 = 0x123 }, .type = BPF_U32 + }; + struct bpf_field a2 = { + .arg = 2, .cmp = EQ, .value = { .v32 = 0x321 }, .type = BPF_U32 + }; struct sock_filter expected[] = { /* l0 */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, arch))), @@ -299,38 +302,40 @@ START_TEST(test_multiple_instance_same_instr) /* l20 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), /* l21 */ LOAD(offsetof(struct seccomp_data, args[2])), /* l22 */ EQ(0x321, 0, 1), - /* l23 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), - /* l24 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + /* l24 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), + /* l23 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /* ----- end call42 ------ */ /* l25 */ LOAD(offsetof(struct seccomp_data, args[1])), /* l26 */ EQ(0x123, 0, 1), - /* l27 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), + /* l24 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), /* l28 */ LOAD(offsetof(struct seccomp_data, args[2])), /* l29 */ EQ(0x321, 0, 1), - /* l30 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), - /* l31 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + /* l31 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), + /* l30 */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /* ----- end call45 ------ */ }; struct sock_filter result[sizeof(expected) / sizeof(expected[0]) + 10]; filter_notify(42); - filter_add_arg(1, a1); - filter_flush_args(); - filter_add_arg(2, a2); - filter_flush_args(); + filter_add_check(&a1); + filter_flush_args(42); + filter_notify(42); + filter_add_check(&a2); + filter_flush_args(42); filter_notify(43); filter_notify(44); filter_notify(45); - filter_add_arg(1, a1); - filter_flush_args(); - filter_add_arg(2, a2); - filter_flush_args(); + filter_add_check(&a1); + filter_flush_args(45); + filter_notify(45); + filter_add_check(&a2); + filter_flush_args(45); filter_notify(46); filter_write(tfilter); size = read_filter(result, tfilter); - ck_assert_uint_eq(size, ARRAY_SIZE(expected)); + bpf_disasm_all(result, size); ck_assert(filter_eq(expected, result, ARRAY_SIZE(expected))); } END_TEST diff --git a/tests/unit/testutil.h b/tests/unit/testutil.h index a0fa0b8..caccd44 100644 --- a/tests/unit/testutil.h +++ b/tests/unit/testutil.h @@ -58,7 +58,7 @@ static inline void *test_gluten_write_ptr(struct gluten *g, ck_assert_ptr_nonnull(p); \ memcpy(&ref, p, sizeof(ref)); \ } while (0) - +#define NS_NUM CONTEXT_TYPE_MAX - 3 struct args_target { long ret; int err; @@ -67,7 +67,7 @@ struct args_target { int fd; int nr; bool filter_args[6]; - struct bpf_arg args[6]; + struct bpf_field bpf_fields[6]; void *targs[6]; void *tclone; bool ns[NS_NUM]; diff --git a/tests/unit/util.c b/tests/unit/util.c index 96d6663..26f13e0 100644 --- a/tests/unit/util.c +++ b/tests/unit/util.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,19 @@ struct gluten gluten; char stderr_buff[BUFSIZ]; char stdout_buff[BUFSIZ]; +#define logfn(name) \ +void name(const char *format, ...) { \ + va_list args; \ + \ + va_start(args, format); \ + (void)vfprintf(stderr, format, args); \ + va_end(args); \ + if (format[strlen(format)] != '\n') \ + fprintf(stderr, "\n"); \ +} + +logfn(debug) + int install_single_syscall(long nr) { /* filter a single syscall for the tests */ @@ -209,7 +223,7 @@ void mock_syscall_target() void set_args_no_check(struct args_target *at) { for (unsigned int i = 0; i < 6; i++) - at->args[i].cmp = NO_CHECK; + at->bpf_fields[i].cmp = NO_CHECK; } static int set_ns_flags(bool ns[], int flags) @@ -217,8 +231,9 @@ static int set_ns_flags(bool ns[], int flags) unsigned int i; for (i = 0; i < NS_NUM; i++) { - if (!ns[i] || i == NS_NONE) + if (!ns[i]) continue; + switch (i) { case NS_CGROUP: flags |= CLONE_NEWCGROUP; -- cgit v1.2.3