In order to support systems that can run applications from multiple architectures we need to be able to support multiple filter DBs; were calling this "filter collections". This patch adds the basic collection support such that it passes all of the existing tests; further work may be necessary once we start using the multiple filter capabilities.
Signed-off-by: Paul Moore <[email protected]> --- src/api.c | 154 ++++++++++++++++++++++++++++------------- src/arch.h | 3 + src/db.c | 216 +++++++++++++++++++++++++++++++++++++++++---------------- src/db.h | 37 +++++++--- src/gen_bpf.c | 14 +++- src/gen_bpf.h | 2 - src/gen_pfc.c | 16 +++- src/gen_pfc.h | 2 - 8 files changed, 312 insertions(+), 132 deletions(-) diff --git a/src/api.c b/src/api.c index 3e7f8d7..f698f54 100644 --- a/src/api.c +++ b/src/api.c @@ -46,7 +46,7 @@ */ static int _ctx_valid(const scmp_filter_ctx *ctx) { - return db_valid((struct db_filter *)ctx); + return db_col_valid((struct db_filter_col *)ctx); } /** @@ -67,20 +67,51 @@ static int _syscall_valid(int syscall) /* NOTE - function header comment in include/seccomp.h */ scmp_filter_ctx seccomp_init(uint32_t def_action) { + struct db_filter_col *col; + struct db_filter *db; + if (db_action_valid(def_action) < 0) return NULL; - return db_init(&arch_def_native, def_action); + col = db_col_init(def_action); + if (col == NULL) + return NULL; + db = db_init(&arch_def_native); + if (db == NULL) + goto init_failure_col; + + if (db_col_db_add(col, db) < 0) + goto init_failure_db; + + return col; + +init_failure_db: + db_release(db); +init_failure_col: + db_col_release(col); + return NULL; } /* NOTE - function header comment in include/seccomp.h */ int seccomp_reset(scmp_filter_ctx ctx, uint32_t def_action) { - if (_ctx_valid(ctx) || db_action_valid(def_action) < 0) + int rc; + struct db_filter_col *col = (struct db_filter_col *)ctx; + struct db_filter *db; + + if (db_col_valid(col) || db_action_valid(def_action) < 0) return -EINVAL; - db_reset((struct db_filter *)ctx, def_action); - return 0; + db_col_reset(col, def_action); + + db = db_init(&arch_def_native); + if (db == NULL) + return -ENOMEM; + rc = db_col_db_add(col, db); + if (rc < 0) + db_release(db); + + return rc; } /* NOTE - function header comment in include/seccomp.h */ @@ -89,25 +120,25 @@ void seccomp_release(scmp_filter_ctx ctx) if (_ctx_valid(ctx)) return; - db_release((struct db_filter *)ctx); + db_col_release((struct db_filter_col *)ctx); } /* NOTE - function header comment in include/seccomp.h */ int seccomp_load(const scmp_filter_ctx ctx) { int rc; - struct db_filter *filter; + struct db_filter_col *col; struct bpf_program *program; if (_ctx_valid(ctx)) return -EINVAL; - filter = (struct db_filter *)ctx; + col = (struct db_filter_col *)ctx; - program = gen_bpf_generate(filter); + program = gen_bpf_generate((struct db_filter_col *)ctx); if (program == NULL) return -ENOMEM; - /* attempt to set NO_NEW_PRIVS but don't fail if it doesn't work */ - if (filter->attr.nnp_enable) { + /* attempt to set NO_NEW_PRIVS */ + if (col->attr.nnp_enable) { rc = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); if (rc < 0) return -errno; @@ -128,7 +159,7 @@ int seccomp_attr_get(const scmp_filter_ctx ctx, if (_ctx_valid(ctx)) return -EINVAL; - return db_attr_get((const struct db_filter *)ctx, attr, value); + return db_col_attr_get((const struct db_filter_col *)ctx, attr, value); } /* NOTE - function header comment in include/seccomp.h */ @@ -138,7 +169,7 @@ int seccomp_attr_set(scmp_filter_ctx ctx, if (_ctx_valid(ctx)) return -EINVAL; - return db_attr_set((struct db_filter *)ctx, attr, value); + return db_col_attr_set((struct db_filter_col *)ctx, attr, value); } /* NOTE - function header comment in include/seccomp.h */ @@ -153,31 +184,46 @@ int seccomp_syscall_resolve_name(const char *name) /* NOTE - function header comment in include/seccomp.h */ int seccomp_syscall_priority(scmp_filter_ctx ctx, int syscall, uint8_t priority) { - int rc; + int rc = 0, rc_tmp; + unsigned int iter; + int syscall_tmp; + struct db_filter_col *col; struct db_filter *filter; if (_ctx_valid(ctx) || _syscall_valid(syscall)) return -EINVAL; - filter = (struct db_filter *)ctx; + col = (struct db_filter_col *)ctx; + + for (iter = 0; iter < col->filter_cnt; iter++) { + filter = col->filters[iter]; + syscall_tmp = syscall; + + rc_tmp = arch_syscall_translate(filter->arch, &syscall_tmp); + if (rc_tmp < 0) + goto syscall_priority_failure; + + /* if this is a pseudo syscall (syscall < 0) then we need to + * rewrite the syscall for some arch specific reason */ + if (syscall_tmp < 0) { + rc_tmp = arch_syscall_rewrite(filter->arch, + &syscall_tmp); + if (rc_tmp < 0) + goto syscall_priority_failure; + } - rc = arch_syscall_translate(filter->arch, &syscall); - if (rc < 0) - return rc; + rc_tmp = db_syscall_priority(filter, syscall_tmp, priority); - /* if this is a pseudo syscall (syscall < 0) then we need to rewrite - * the syscall for some arch specific reason */ - if (syscall < 0) { - rc = arch_syscall_rewrite(filter->arch, &syscall); - if (rc < 0) - return rc; +syscall_priority_failure: + if (rc == 0 && rc_tmp < 0) + rc = rc_tmp; } - return db_syscall_priority(filter, syscall, priority); + return rc; } /** * Add a new rule to the current filter - * @param filter the DB filter + * @param col the filter collection * @param strict the strict flag * @param action the filter action * @param syscall the syscall number @@ -193,32 +239,30 @@ int seccomp_syscall_priority(scmp_filter_ctx ctx, int syscall, uint8_t priority) * zero on success, negative values on failure. * */ -static int _seccomp_rule_add(struct db_filter *filter, +static int _seccomp_rule_add(struct db_filter_col *col, unsigned int strict, uint32_t action, int syscall, unsigned int arg_cnt, va_list arg_list) { - int rc; + int rc = 0, rc_tmp; + int syscall_tmp; unsigned int iter; unsigned int chain_len_max; unsigned int arg_num; + struct db_filter *filter; struct db_api_arg *chain = NULL; struct scmp_arg_cmp arg_data; - if (db_valid(filter) || _syscall_valid(syscall)) + if (db_col_valid(col) || _syscall_valid(syscall)) return -EINVAL; rc = db_action_valid(action); if (rc < 0) return rc; - if (action == filter->attr.act_default) + if (action == col->attr.act_default) return -EPERM; - rc = arch_syscall_translate(filter->arch, &syscall); - if (rc < 0) - return rc; - /* collect the arguments for the filter rule */ - chain_len_max = arch_arg_count_max(filter->arch); + chain_len_max = ARG_COUNT_MAX; chain = malloc(sizeof(*chain) * chain_len_max); if (chain == NULL) return -ENOMEM; @@ -256,16 +300,30 @@ static int _seccomp_rule_add(struct db_filter *filter, } } - /* if this is a pseudo syscall (syscall < 0) then we need to rewrite - * the rule for some arch specific reason */ - if (syscall < 0) { - rc = arch_filter_rewrite(filter->arch, strict, &syscall, chain); - if (rc < 0) - goto rule_add_return; - } + for (iter = 0; iter < col->filter_cnt; iter++) { + filter = col->filters[iter]; + syscall_tmp = syscall; + + rc_tmp = arch_syscall_translate(filter->arch, &syscall_tmp); + if (rc_tmp < 0) + goto rule_add_failure; + + /* if this is a pseudo syscall (syscall < 0) then we need to + * rewrite the rule for some arch specific reason */ + if (syscall_tmp < 0) { + rc_tmp = arch_filter_rewrite(filter->arch, strict, + &syscall_tmp, chain); + if (rc_tmp < 0) + goto rule_add_failure; + } - /* add the new rule to the existing filter */ - rc = db_rule_add(filter, action, syscall, chain); + /* add the new rule to the existing filter */ + rc_tmp = db_rule_add(filter, action, syscall_tmp, chain); + +rule_add_failure: + if (rc == 0 && rc_tmp < 0) + rc = rc_tmp; + } rule_add_return: if (chain != NULL) @@ -281,7 +339,7 @@ int seccomp_rule_add(scmp_filter_ctx ctx, va_list arg_list; va_start(arg_list, arg_cnt); - rc = _seccomp_rule_add((struct db_filter *)ctx, + rc = _seccomp_rule_add((struct db_filter_col *)ctx, 0, action, syscall, arg_cnt, arg_list); va_end(arg_list); @@ -296,7 +354,7 @@ int seccomp_rule_add_exact(scmp_filter_ctx ctx, uint32_t action, va_list arg_list; va_start(arg_list, arg_cnt); - rc = _seccomp_rule_add((struct db_filter *)ctx, + rc = _seccomp_rule_add((struct db_filter_col *)ctx, 1, action, syscall, arg_cnt, arg_list); va_end(arg_list); @@ -309,7 +367,7 @@ int seccomp_export_pfc(const scmp_filter_ctx ctx, int fd) if (_ctx_valid(ctx)) return -EINVAL; - return gen_pfc_generate((struct db_filter *)ctx, fd); + return gen_pfc_generate((struct db_filter_col *)ctx, fd); } /* NOTE - function header comment in include/seccomp.h */ @@ -321,7 +379,7 @@ int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd) if (_ctx_valid(ctx)) return -EINVAL; - program = gen_bpf_generate((struct db_filter *)ctx); + program = gen_bpf_generate((struct db_filter_col *)ctx); if (program == NULL) return -ENOMEM; rc = write(fd, program->blks, BPF_PGM_SIZE(program)); diff --git a/src/arch.h b/src/arch.h index 4e14ca6..43ef19f 100644 --- a/src/arch.h +++ b/src/arch.h @@ -71,8 +71,9 @@ struct arch_syscall_def { #define D64_LO(x) ((uint32_t)((uint64_t)(x) & 0x00000000ffffffff)) #define D64_HI(x) ((uint32_t)((uint64_t)(x) >> 32)) -int arch_arg_count_max(const struct arch_def *arch); +#define ARG_COUNT_MAX 6 +int arch_arg_count_max(const struct arch_def *arch); /** * Determine the argument offset diff --git a/src/db.c b/src/db.c index 1468cf9..174c95c 100644 --- a/src/db.c +++ b/src/db.c @@ -311,108 +311,101 @@ int db_action_valid(uint32_t action) } /** - * Free and reset the seccomp filter DB - * @param db the seccomp filter DB + * Free and reset the seccomp filter collection + * @param col the seccomp filter collection * @param def_action the default filter action * - * This function frees any existing filters and resets the filter DB to a - * default state; only the DB architecture is preserved. + * This function frees any existing filter DBs and resets the collection to a + * default state. * */ -void db_reset(struct db_filter *db, uint32_t def_action) +void db_col_reset(struct db_filter_col *col, uint32_t def_action) { - struct db_sys_list *s_iter; + unsigned int iter; - if (db == NULL) + if (col == NULL) return; /* free any filters */ - if (db->syscalls != NULL) { - s_iter = db->syscalls; - while (s_iter != NULL) { - db->syscalls = s_iter->next; - _db_tree_free(s_iter->chains); - free(s_iter); - s_iter = db->syscalls; - } - db->syscalls = NULL; - } + for (iter = 0; iter < col->filter_cnt; iter++) + db_release(col->filters[iter]); + col->filter_cnt = 0; + free(col->filters); + col->filters = NULL; /* set the default attribute values */ - db->attr.act_default = def_action; - db->attr.act_badarch = SCMP_ACT_KILL; - db->attr.nnp_enable = 1; + col->attr.act_default = def_action; + col->attr.act_badarch = SCMP_ACT_KILL; + col->attr.nnp_enable = 1; /* set the state */ - db->state = _DB_STA_VALID; + col->state = _DB_STA_VALID; } /** - * Intitalize a seccomp filter DB - * @param arch the architecture definition + * Intitalize a seccomp filter collection * @param def_action the default filter action * - * This function initializes a seccomp filter DB and readies it for use. - * Returns a pointer to the DB on success, NULL on failure. + * This function initializes a seccomp filter collection and readies it for + * use. Returns a pointer to the collection on success, NULL on failure. * */ -struct db_filter *db_init(const struct arch_def *arch, uint32_t def_action) +struct db_filter_col *db_col_init(uint32_t def_action) { - struct db_filter *db; + struct db_filter_col *col; - db = malloc(sizeof(*db)); - if (db == NULL) + col = malloc(sizeof(*col)); + if (col == NULL) return NULL; - /* clear the buffer for the first time and set the arch */ - memset(db, 0, sizeof(*db)); - db->arch = arch; + /* clear the buffer for the first time */ + memset(col, 0, sizeof(*col)); /* reset the DB to a known state */ - db_reset(db, def_action); + db_col_reset(col, def_action); - return db; + return col; } /** - * Destroy a seccomp filter DB - * @param db the seccomp filter DB + * Destroy a seccomp filter collection + * @param col the seccomp filter collection * - * This function destroys a seccomp filter DB. After calling this function, - * the filter should no longer be referenced. + * This function destroys a seccomp filter collection. After calling this + * function, the filter should no longer be referenced. * */ -void db_release(struct db_filter *db) +void db_col_release(struct db_filter_col *col) { - if (db == NULL) + if (col == NULL) return; /* set the state, just in case */ - db->state = _DB_STA_FREED; + col->state = _DB_STA_FREED; /* free and reset the DB */ - db_reset(db, 0); - free(db); + db_col_reset(col, 0); + free(col); } /** - * Validate a filter DB - * @param db the seccomp filter DB + * Validate a filter collection + * @param col the seccomp filter collection * - * This function validates a seccomp filter DB. Returns zero if the DB is - * valid, negative values on failure. + * This function validates a seccomp filter collection. Returns zero if the + * collection is valid, negative values on failure. * */ -int db_valid(struct db_filter *db) +int db_col_valid(struct db_filter_col *col) { - if (db != NULL && db->state == _DB_STA_VALID) + if (col != NULL && col->state == _DB_STA_VALID) return 0; return -EINVAL; } /** * Get a filter attribute - * @param db the seccomp filter DB + * @param col the seccomp filter collection * @param attr the filter attribute * @param value the filter attribute value * @@ -420,18 +413,18 @@ int db_valid(struct db_filter *db) * on success, negative values on failure. * */ -int db_attr_get(const struct db_filter *db, - enum scmp_filter_attr attr, uint32_t *value) +int db_col_attr_get(const struct db_filter_col *col, + enum scmp_filter_attr attr, uint32_t *value) { switch (attr) { case SCMP_FLTATR_ACT_DEFAULT: - *value = db->attr.act_default; + *value = col->attr.act_default; break; case SCMP_FLTATR_ACT_BADARCH: - *value = db->attr.act_badarch; + *value = col->attr.act_badarch; break; case SCMP_FLTATR_CTL_NNP: - *value = db->attr.nnp_enable; + *value = col->attr.nnp_enable; break; default: return -EEXIST; @@ -443,7 +436,7 @@ int db_attr_get(const struct db_filter *db, /** * Set a filter attribute - * @param db the seccomp filter DB + * @param db the seccomp filter collection * @param attr the filter attribute * @param value the filter attribute value * @@ -451,8 +444,8 @@ int db_attr_get(const struct db_filter *db, * success, negative values on failure. * */ -int db_attr_set(struct db_filter *db, - enum scmp_filter_attr attr, uint32_t value) +int db_col_attr_set(struct db_filter_col *col, + enum scmp_filter_attr attr, uint32_t value) { switch (attr) { case SCMP_FLTATR_ACT_DEFAULT: @@ -461,12 +454,12 @@ int db_attr_set(struct db_filter *db, break; case SCMP_FLTATR_ACT_BADARCH: if (db_action_valid(value) == 0) - db->attr.act_badarch = value; + col->attr.act_badarch = value; else return -EINVAL; break; case SCMP_FLTATR_CTL_NNP: - db->attr.nnp_enable = (value ? 1 : 0); + col->attr.nnp_enable = (value ? 1 : 0); break; default: return -EEXIST; @@ -477,6 +470,109 @@ int db_attr_set(struct db_filter *db, } /** + * Add a new filter DB to a filter collection + * @param col the seccomp filter collection + * @param db the seccomp filter DB + * + * This function adds an existing seccomp filter DB to an existing seccomp + * filter collection assuming there isn't a filter DB already present with the + * same architecture. Returns zero on success, negative values on failure. + * + */ +int db_col_db_add(struct db_filter_col *col, struct db_filter *db) +{ + unsigned int iter; + struct db_filter **dbs; + + for (iter = 0; iter < col->filter_cnt; iter++) + if (col->filters[iter]->arch->token == db->arch->token) + return -EEXIST; + + dbs = realloc(col->filters, + sizeof(struct db_filter *) * (col->filter_cnt + 1)); + if (dbs == NULL) + return -ENOMEM; + col->filters = dbs; + col->filter_cnt++; + col->filters[col->filter_cnt - 1] = db; + + return 0; +} + +/** + * Free and reset the seccomp filter DB + * @param db the seccomp filter DB + * @param def_action the default filter action + * + * This function frees any existing filters and resets the filter DB to a + * default state; only the DB architecture is preserved. + * + */ +void db_reset(struct db_filter *db) +{ + struct db_sys_list *s_iter; + + if (db == NULL) + return; + + /* free any filters */ + if (db->syscalls != NULL) { + s_iter = db->syscalls; + while (s_iter != NULL) { + db->syscalls = s_iter->next; + _db_tree_free(s_iter->chains); + free(s_iter); + s_iter = db->syscalls; + } + db->syscalls = NULL; + } +} + +/** + * Intitalize a seccomp filter DB + * @param arch the architecture definition + * + * This function initializes a seccomp filter DB and readies it for use. + * Returns a pointer to the DB on success, NULL on failure. + * + */ +struct db_filter *db_init(const struct arch_def *arch) +{ + struct db_filter *db; + + db = malloc(sizeof(*db)); + if (db == NULL) + return NULL; + + /* clear the buffer for the first time and set the arch */ + memset(db, 0, sizeof(*db)); + db->arch = arch; + + /* reset the DB to a known state */ + db_reset(db); + + return db; +} + +/** + * Destroy a seccomp filter DB + * @param db the seccomp filter DB + * + * This function destroys a seccomp filter DB. After calling this function, + * the filter should no longer be referenced. + * + */ +void db_release(struct db_filter *db) +{ + if (db == NULL) + return; + + /* free and reset the DB */ + db_reset(db); + free(db); +} + +/** * Update the user specified portion of the syscall priority * @param db the seccomp filter db * @param syscall the syscall number diff --git a/src/db.h b/src/db.h index b60470d..a9ebbc9 100644 --- a/src/db.h +++ b/src/db.h @@ -131,16 +131,23 @@ struct db_filter_attr { }; struct db_filter { + /* target architecture */ + const struct arch_def *arch; + + /* syscall filters, kept as a sorted single-linked list */ + struct db_sys_list *syscalls; +}; + +struct db_filter_col { /* verification / state */ int state; - /* target architecture */ - const struct arch_def *arch; /* attributes */ struct db_filter_attr attr; - /* syscall filters, kept as a sorted single-linked list */ - struct db_sys_list *syscalls; + /* individual filters */ + struct db_filter **filters; + unsigned int filter_cnt; }; /** @@ -157,16 +164,22 @@ struct db_filter { int db_action_valid(uint32_t action); -void db_reset(struct db_filter *db, uint32_t def_action); -struct db_filter *db_init(const struct arch_def *arch, uint32_t def_action); -void db_release(struct db_filter *db); +struct db_filter_col *db_col_init(uint32_t def_action); +void db_col_reset(struct db_filter_col *col, uint32_t def_action); +void db_col_release(struct db_filter_col *col); -int db_valid(struct db_filter *db); +int db_col_valid(struct db_filter_col *col); -int db_attr_get(const struct db_filter *db, - enum scmp_filter_attr attr, uint32_t *value); -int db_attr_set(struct db_filter *db, - enum scmp_filter_attr attr, uint32_t value); +int db_col_attr_get(const struct db_filter_col *col, + enum scmp_filter_attr attr, uint32_t *value); +int db_col_attr_set(struct db_filter_col *col, + enum scmp_filter_attr attr, uint32_t value); + +int db_col_db_add(struct db_filter_col *col, struct db_filter *db); + +struct db_filter *db_init(const struct arch_def *arch); +void db_reset(struct db_filter *db); +void db_release(struct db_filter *db); int db_syscall_priority(struct db_filter *db, unsigned int syscall, uint8_t priority); diff --git a/src/gen_bpf.c b/src/gen_bpf.c index 9e2f809..2e8407e 100644 --- a/src/gen_bpf.c +++ b/src/gen_bpf.c @@ -1459,20 +1459,26 @@ build_bpf_free_blks: /** * Generate a BPF representation of the filter DB - * @param db the seccomp filter DB + * @param col the seccomp filter collection * - * This function generates a BPF representation of the given filter DB. + * This function generates a BPF representation of the given filter collection. * Returns a pointer to a valid bpf_program on success, NULL on failure. * */ -struct bpf_program *gen_bpf_generate(const struct db_filter *db) +struct bpf_program *gen_bpf_generate(const struct db_filter_col *col) { int rc; + struct db_filter *db; struct bpf_state state; + /* NOTE: temporary until we fully support filter collections */ + if (col->filter_cnt != 1 || col->filters[0]->arch != &arch_def_native) + return NULL; + db = col->filters[0]; + memset(&state, 0, sizeof(state)); state.arch = db->arch; - state.attr = &db->attr; + state.attr = &col->attr; state.bpf = malloc(sizeof(*(state.bpf))); if (state.bpf == NULL) diff --git a/src/gen_bpf.h b/src/gen_bpf.h index 92828ee..43f337e 100644 --- a/src/gen_bpf.h +++ b/src/gen_bpf.h @@ -35,7 +35,7 @@ struct bpf_program { #define BPF_PGM_SIZE(x) \ ((x)->blk_cnt * sizeof(*((x)->blks))) -struct bpf_program *gen_bpf_generate(const struct db_filter *db); +struct bpf_program *gen_bpf_generate(const struct db_filter_col *col); void gen_bpf_release(struct bpf_program *program); #endif diff --git a/src/gen_pfc.c b/src/gen_pfc.c index 4334c14..3f36f11 100644 --- a/src/gen_pfc.c +++ b/src/gen_pfc.c @@ -198,22 +198,28 @@ static void _gen_pfc_syscall(const struct arch_def *arch, /** * Generate a pseudo filter code string representation - * @param db the seccomp filter DB + * @param col the seccomp filter collection * @param fd the fd to send the output * * This function generates a pseudo filter code representation of the given - * filter DB and writes it to the given fd. Returns zero on success, negative - * values on failure. + * filter collection and writes it to the given fd. Returns zero on success, + * negative values on failure. * */ -int gen_pfc_generate(const struct db_filter *db, int fd) +int gen_pfc_generate(const struct db_filter_col *col, int fd) { int rc = 0; int newfd; FILE *fds; + struct db_filter *db; struct db_sys_list *s_iter; struct pfc_sys_list *p_iter = NULL, *p_new, *p_head = NULL, *p_prev; + /* NOTE: temporary until we fully support filter collections */ + if (col->filter_cnt != 1 || col->filters[0]->arch != &arch_def_native) + return -EFAULT; + db = col->filters[0]; + newfd = dup(fd); if (newfd < 0) return errno; @@ -263,7 +269,7 @@ int gen_pfc_generate(const struct db_filter *db, int fd) p_iter = p_iter->next; } fprintf(fds, "# default action\n"); - _pfc_action(fds, db->attr.act_default); + _pfc_action(fds, col->attr.act_default); fprintf(fds, "#\n"); fprintf(fds, "# pseudo filter code end\n"); fprintf(fds, "#\n"); diff --git a/src/gen_pfc.h b/src/gen_pfc.h index b0b2195..80f680f 100644 --- a/src/gen_pfc.h +++ b/src/gen_pfc.h @@ -24,6 +24,6 @@ #include "db.h" -int gen_pfc_generate(const struct db_filter *db, int fd); +int gen_pfc_generate(const struct db_filter_col *col, int fd); #endif ------------------------------------------------------------------------------ Got visibility? Most devs has no idea what their production app looks like. Find out how fast your code is with AppDynamics Lite. http://ad.doubleclick.net/clk;262219671;13503038;y? http://info.appdynamics.com/FreeJavaPerformanceDownload.html _______________________________________________ libseccomp-discuss mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/libseccomp-discuss
