Control: tags -1 + fixed-upstream patch On 2019-11-04 21:14 +0100, Sven Joachim wrote:
> Control: reassign -1 libseccomp2 2.4.1-1 > Control: forwarded -1 https://github.com/seccomp/libseccomp/issues/153 > Control: retitle -1 libseccomp2: seccomp_rule_add is very slow > Control: affects -1 man-db lintian > > Reassigning to libseccomp2 now. So the upstream bug has been closed in the master branch, and I can confirm that cherry-picking the two commits mentioned in [1] works. This also causes libseccomp's own testsuite to complete much faster. A patch is attached, if a merge request on salsa is preferred, please say so. Cheers, Sven 1. https://github.com/seccomp/libseccomp/pull/180#issuecomment-552650906
From 319456e038ee5b881829bed6537a060d3db2e176 Mon Sep 17 00:00:00 2001 From: Sven Joachim <svenj...@gmx.de> Date: Thu, 14 Nov 2019 18:22:08 +0100 Subject: [PATCH 1/1] Cherry-pick upstream commits 21b98d8 and 19af04d For details see https://github.com/seccomp/libseccomp/pull/180#issuecomment-552650906. Closes: #943913 --- .../patches/db-add-shadow-transactions.patch | 209 ++++++++++++++++++ ...ome-of-the-code-which-adds-rules-to-.patch | 158 +++++++++++++ debian/patches/series | 2 + 3 files changed, 369 insertions(+) create mode 100644 debian/patches/db-add-shadow-transactions.patch create mode 100644 debian/patches/db-consolidate-some-of-the-code-which-adds-rules-to-.patch diff --git a/debian/patches/db-add-shadow-transactions.patch b/debian/patches/db-add-shadow-transactions.patch new file mode 100644 index 0000000..0847287 --- /dev/null +++ b/debian/patches/db-add-shadow-transactions.patch @@ -0,0 +1,209 @@ +From 19af04da86e9a4168a443f3563fc7aec8839edf0 Mon Sep 17 00:00:00 2001 +From: Paul Moore <p...@paul-moore.com> +Date: Mon, 4 Nov 2019 20:15:20 -0500 +Subject: [PATCH 2/2] db: add shadow transactions + +Creating a transaction can be very time consuming on large filters since we +create a duplicate filter tree iteratively using the rules supplied by the +caller. In an effort to speed this up we introduce the idea of shadow +transactions where on a successful transaction commit we preserve the old +transaction checkpoint and bring it up to date with the current filter and +save it for future use. The next time we start a new transaction we check +to see if a shadow transaction exists, if it does we use that instead of +creating a new transaction checkpoint from scratch. + +Acked-by: Tom Hromatka <tom.hroma...@oracle.com> +Signed-off-by: Paul Moore <p...@paul-moore.com> +--- + src/db.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- + src/db.h | 1 + + 2 files changed, 127 insertions(+), 1 deletion(-) + +diff --git a/src/db.c b/src/db.c +index 6a30c64..a40cb2b 100644 +--- a/src/db.c ++++ b/src/db.c +@@ -909,6 +909,9 @@ static void _db_snap_release(struct db_filter_snap *snap) + { + unsigned int iter; + ++ if (snap == NULL) ++ return; ++ + if (snap->filter_cnt > 0) { + for (iter = 0; iter < snap->filter_cnt; iter++) { + if (snap->filters[iter]) +@@ -1134,6 +1137,7 @@ init_failure: + void db_col_release(struct db_filter_col *col) + { + unsigned int iter; ++ struct db_filter_snap *snap; + + if (col == NULL) + return; +@@ -1141,6 +1145,13 @@ void db_col_release(struct db_filter_col *col) + /* set the state, just in case */ + col->state = _DB_STA_FREED; + ++ /* free any snapshots */ ++ while (col->snapshots != NULL) { ++ snap = col->snapshots; ++ col->snapshots = snap->next; ++ _db_snap_release(snap); ++ } ++ + /* free any filters */ + for (iter = 0; iter < col->filter_cnt; iter++) + _db_release(col->filters[iter]); +@@ -2343,6 +2354,20 @@ int db_col_transaction_start(struct db_filter_col *col) + struct db_filter *filter_o, *filter_s; + struct db_api_rule_list *rule_o, *rule_s = NULL; + ++ /* check to see if a shadow snapshot exists */ ++ if (col->snapshots && col->snapshots->shadow) { ++ /* we have a shadow! this will be easy */ ++ ++ /* NOTE: we don't bother to do any verification of the shadow ++ * because we start a new transaction every time we add ++ * a new rule to the filter(s); if this ever changes we ++ * will need to add a mechanism to verify that the shadow ++ * transaction is current/correct */ ++ ++ col->snapshots->shadow = false; ++ return 0; ++ } ++ + /* allocate the snapshot */ + snap = zmalloc(sizeof(*snap)); + if (snap == NULL) +@@ -2436,14 +2461,114 @@ void db_col_transaction_abort(struct db_filter_col *col) + * Commit the top most seccomp filter transaction + * @param col the filter collection + * +- * This function commits the most recent seccomp filter transaction. ++ * This function commits the most recent seccomp filter transaction and ++ * attempts to create a shadow transaction that is a duplicate of the current ++ * filter to speed up future transactions. + * + */ + void db_col_transaction_commit(struct db_filter_col *col) + { ++ int rc; ++ unsigned int iter; + struct db_filter_snap *snap; ++ struct db_filter *filter_o, *filter_s; ++ struct db_api_rule_list *rule_o, *rule_s; + + snap = col->snapshots; ++ if (snap == NULL) ++ return; ++ ++ /* check for a shadow set by a higher transaction commit */ ++ if (snap->shadow) { ++ /* leave the shadow intact, but drop the next snapshot */ ++ if (snap->next) { ++ snap->next = snap->next->next; ++ _db_snap_release(snap->next); ++ } ++ return; ++ } ++ ++ /* adjust the number of filters if needed */ ++ if (col->filter_cnt > snap->filter_cnt) { ++ unsigned int tmp_i; ++ struct db_filter **tmp_f; ++ ++ /* add filters */ ++ tmp_f = realloc(snap->filters, ++ sizeof(struct db_filter *) * col->filter_cnt); ++ if (tmp_f == NULL) ++ goto shadow_err; ++ snap->filters = tmp_f; ++ do { ++ tmp_i = snap->filter_cnt; ++ snap->filters[tmp_i] = ++ _db_init(col->filters[tmp_i]->arch); ++ if (snap->filters[tmp_i] == NULL) ++ goto shadow_err; ++ snap->filter_cnt++; ++ } while (snap->filter_cnt < col->filter_cnt); ++ } else if (col->filter_cnt < snap->filter_cnt) { ++ /* remove filters */ ++ ++ /* NOTE: while we release the filters we no longer need, we ++ * don't bother to resize the filter array, we just ++ * adjust the filter counter, this *should* be harmless ++ * at the cost of a not reaping all the memory possible */ ++ ++ do { ++ _db_release(snap->filters[snap->filter_cnt--]); ++ } while (snap->filter_cnt > col->filter_cnt); ++ } ++ ++ /* loop through each filter and update the rules on the snapshot */ ++ for (iter = 0; iter < col->filter_cnt; iter++) { ++ filter_o = col->filters[iter]; ++ filter_s = snap->filters[iter]; ++ ++ /* skip ahead to the new rule(s) */ ++ rule_o = filter_o->rules; ++ rule_s = filter_s->rules; ++ if (rule_o == NULL) ++ /* nothing to shadow */ ++ continue; ++ if (rule_s != NULL) { ++ do { ++ rule_o = rule_o->next; ++ rule_s = rule_s->next; ++ } while (rule_s != filter_s->rules); ++ ++ /* did we actually add any rules? */ ++ if (rule_o == filter_o->rules) ++ /* no, we are done in this case */ ++ continue; ++ } ++ ++ /* update the old snapshot to make it a shadow */ ++ do { ++ /* duplicate the rule */ ++ rule_s = db_rule_dup(rule_o); ++ if (rule_s == NULL) ++ goto shadow_err; ++ ++ /* add the rule */ ++ rc = _db_col_rule_add(filter_s, rule_s); ++ if (rc != 0) { ++ free(rule_s); ++ goto shadow_err; ++ } ++ ++ /* next rule */ ++ rule_o = rule_o->next; ++ } while (rule_o != filter_o->rules); ++ } ++ ++ /* success, mark the snapshot as a shadow and return */ ++ snap->shadow = true; ++ return; ++ ++shadow_err: ++ /* we failed making a shadow, cleanup and return */ + col->snapshots = snap->next; + _db_snap_release(snap); ++ return; + } +diff --git a/src/db.h b/src/db.h +index c181038..9dce65a 100644 +--- a/src/db.h ++++ b/src/db.h +@@ -135,6 +135,7 @@ struct db_filter_snap { + /* individual filters */ + struct db_filter **filters; + unsigned int filter_cnt; ++ bool shadow; + + struct db_filter_snap *next; + }; +-- +2.24.0 + diff --git a/debian/patches/db-consolidate-some-of-the-code-which-adds-rules-to-.patch b/debian/patches/db-consolidate-some-of-the-code-which-adds-rules-to-.patch new file mode 100644 index 0000000..09e9009 --- /dev/null +++ b/debian/patches/db-consolidate-some-of-the-code-which-adds-rules-to-.patch @@ -0,0 +1,158 @@ +From 21b98d85e8bfdb701a5f9afd54ff5175af910a45 Mon Sep 17 00:00:00 2001 +From: Paul Moore <p...@paul-moore.com> +Date: Fri, 1 Nov 2019 12:05:58 -0400 +Subject: [PATCH 1/2] db: consolidate some of the code which adds rules to a + single filter + +Pay back some of the technical debt in db_col_rule_add(), no logic +changes in this patch, just removing some code duplication. + +Acked-by: Tom Hromatka <tom.hroma...@oracle.com> +Signed-off-by: Paul Moore <p...@paul-moore.com> +--- + src/db.c | 85 +++++++++++++++++++++++++++++--------------------------- + 1 file changed, 44 insertions(+), 41 deletions(-) + +diff --git a/src/db.c b/src/db.c +index 03e1ba3..6a30c64 100644 +--- a/src/db.c ++++ b/src/db.c +@@ -2179,6 +2179,44 @@ priority_failure: + return rc; + } + ++/** ++ * Add a new rule to a single filter ++ * @param filter the filter ++ * @param rule the filter rule ++ * ++ * This is a helper function for db_col_rule_add() and similar functions, it ++ * isn't generally useful. Returns zero on success, negative values on error. ++ * ++ */ ++static int _db_col_rule_add(struct db_filter *filter, ++ struct db_api_rule_list *rule) ++{ ++ int rc; ++ struct db_api_rule_list *iter; ++ ++ /* add the rule to the filter */ ++ rc = arch_filter_rule_add(filter, rule); ++ if (rc != 0) ++ return rc; ++ ++ /* insert the chain to the end of the rule list */ ++ iter = rule; ++ while (iter->next) ++ iter = iter->next; ++ if (filter->rules != NULL) { ++ rule->prev = filter->rules->prev; ++ iter->next = filter->rules; ++ filter->rules->prev->next = rule; ++ filter->rules->prev = iter; ++ } else { ++ rule->prev = iter; ++ iter->next = rule; ++ filter->rules = rule; ++ } ++ ++ return 0; ++} ++ + /** + * Add a new rule to the current filter + * @param col the filter collection +@@ -2207,7 +2245,7 @@ int db_col_rule_add(struct db_filter_col *col, + size_t chain_size; + struct db_api_arg *chain = NULL; + struct scmp_arg_cmp arg_data; +- struct db_api_rule_list *rule, *rule_tmp; ++ struct db_api_rule_list *rule; + struct db_filter *db; + + /* collect the arguments for the filter rule */ +@@ -2255,9 +2293,6 @@ int db_col_rule_add(struct db_filter_col *col, + + /* add the rule to the different filters in the collection */ + for (iter = 0; iter < col->filter_cnt; iter++) { +- +- /* TODO: consolidate with db_col_transaction_start() */ +- + db = col->filters[iter]; + + /* create the rule */ +@@ -2268,24 +2303,10 @@ int db_col_rule_add(struct db_filter_col *col, + } + + /* add the rule */ +- rc_tmp = arch_filter_rule_add(db, rule); +- if (rc_tmp == 0) { +- /* insert the chain to the end of the rule list */ +- rule_tmp = rule; +- while (rule_tmp->next) +- rule_tmp = rule_tmp->next; +- if (db->rules != NULL) { +- rule->prev = db->rules->prev; +- rule_tmp->next = db->rules; +- db->rules->prev->next = rule; +- db->rules->prev = rule_tmp; +- } else { +- rule->prev = rule_tmp; +- rule_tmp->next = rule; +- db->rules = rule; +- } +- } else ++ rc_tmp = _db_col_rule_add(db, rule); ++ if (rc_tmp != 0) + free(rule); ++ + add_arch_fail: + if (rc_tmp != 0 && rc == 0) + rc = rc_tmp; +@@ -2320,7 +2341,7 @@ int db_col_transaction_start(struct db_filter_col *col) + unsigned int iter; + struct db_filter_snap *snap; + struct db_filter *filter_o, *filter_s; +- struct db_api_rule_list *rule_o, *rule_s = NULL, *rule_tmp; ++ struct db_api_rule_list *rule_o, *rule_s = NULL; + + /* allocate the snapshot */ + snap = zmalloc(sizeof(*snap)); +@@ -2350,33 +2371,15 @@ int db_col_transaction_start(struct db_filter_col *col) + if (rule_o == NULL) + continue; + do { +- +- /* TODO: consolidate with db_col_rule_add() */ +- + /* duplicate the rule */ + rule_s = db_rule_dup(rule_o); + if (rule_s == NULL) + goto trans_start_failure; + + /* add the rule */ +- rc = arch_filter_rule_add(filter_s, rule_s); ++ rc = _db_col_rule_add(filter_s, rule_s); + if (rc != 0) + goto trans_start_failure; +- +- /* insert the chain to the end of the rule list */ +- rule_tmp = rule_s; +- while (rule_tmp->next) +- rule_tmp = rule_tmp->next; +- if (filter_s->rules != NULL) { +- rule_s->prev = filter_s->rules->prev; +- rule_tmp->next = filter_s->rules; +- filter_s->rules->prev->next = rule_s; +- filter_s->rules->prev = rule_tmp; +- } else { +- rule_s->prev = rule_tmp; +- rule_tmp->next = rule_s; +- filter_s->rules = rule_s; +- } + rule_s = NULL; + + /* next rule */ +-- +2.24.0 + diff --git a/debian/patches/series b/debian/patches/series index bbdb514..0f07bf5 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,3 +1,5 @@ tests-rely-on-__SNR_xxx-instead-of-__NR_xxx-for-sysc.patch cython3.patch api_define__SNR_ppoll_again.patch +db-consolidate-some-of-the-code-which-adds-rules-to-.patch +db-add-shadow-transactions.patch -- 2.24.0