From: Greg Kroah-Hartman Date: Thu, 5 Jul 2018 16:14:45 +0000 (+0200) Subject: 4.14-stable patches X-Git-Tag: v4.14.54~18 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=74c857943357ccc4a90ebbd509984ff5d5af44d5;p=thirdparty%2Fkernel%2Fstable-queue.git 4.14-stable patches added patches: netfilter-nf_tables-add-missing-netlink-attrs-to-policies.patch netfilter-nf_tables-bogus-ebusy-in-chain-deletions.patch netfilter-nf_tables-disable-preemption-in-nft_update_chain_stats.patch netfilter-nf_tables-don-t-assume-chain-stats-are-set-when-jumplabel-is-set.patch netfilter-nf_tables-fix-memory-leak-on-error-exit-return.patch netfilter-nf_tables-increase-nft_counters_enabled-in-nft_chain_stats_replace.patch netfilter-nf_tables-nft_compat-fix-refcount-leak-on-xt-module.patch netfilter-nft_compat-fix-handling-of-large-matchinfo-size.patch netfilter-nft_compat-prepare-for-indirect-info-storage.patch netfilter-nft_meta-fix-wrong-value-dereference-in-nft_meta_set_eval.patch --- diff --git a/queue-4.14/netfilter-nf_tables-add-missing-netlink-attrs-to-policies.patch b/queue-4.14/netfilter-nf_tables-add-missing-netlink-attrs-to-policies.patch new file mode 100644 index 00000000000..864e73e6a07 --- /dev/null +++ b/queue-4.14/netfilter-nf_tables-add-missing-netlink-attrs-to-policies.patch @@ -0,0 +1,39 @@ +From 467697d289e7e6e1b15910d99096c0da08c56d5b Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Tue, 20 Mar 2018 15:25:37 +0100 +Subject: netfilter: nf_tables: add missing netlink attrs to policies + +From: Florian Westphal + +commit 467697d289e7e6e1b15910d99096c0da08c56d5b upstream. + +Fixes: 8aeff920dcc9 ("netfilter: nf_tables: add stateful object reference to set elements") +Fixes: f25ad2e907f1 ("netfilter: nf_tables: prepare for expressions associated to set elements") +Fixes: 1a94e38d254b ("netfilter: nf_tables: add NFTA_RULE_ID attribute") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/netfilter/nf_tables_api.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -1978,6 +1978,7 @@ static const struct nla_policy nft_rule_ + [NFTA_RULE_POSITION] = { .type = NLA_U64 }, + [NFTA_RULE_USERDATA] = { .type = NLA_BINARY, + .len = NFT_USERDATA_MAXLEN }, ++ [NFTA_RULE_ID] = { .type = NLA_U32 }, + }; + + static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net, +@@ -3412,6 +3413,8 @@ static const struct nla_policy nft_set_e + [NFTA_SET_ELEM_TIMEOUT] = { .type = NLA_U64 }, + [NFTA_SET_ELEM_USERDATA] = { .type = NLA_BINARY, + .len = NFT_USERDATA_MAXLEN }, ++ [NFTA_SET_ELEM_EXPR] = { .type = NLA_NESTED }, ++ [NFTA_SET_ELEM_OBJREF] = { .type = NLA_STRING }, + }; + + static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = { diff --git a/queue-4.14/netfilter-nf_tables-bogus-ebusy-in-chain-deletions.patch b/queue-4.14/netfilter-nf_tables-bogus-ebusy-in-chain-deletions.patch new file mode 100644 index 00000000000..c3d17fd0b77 --- /dev/null +++ b/queue-4.14/netfilter-nf_tables-bogus-ebusy-in-chain-deletions.patch @@ -0,0 +1,183 @@ +From bb7b40aecbf778c0c83a5bd62b0f03ca9f49a618 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Tue, 8 May 2018 02:43:57 +0200 +Subject: netfilter: nf_tables: bogus EBUSY in chain deletions + +From: Pablo Neira Ayuso + +commit bb7b40aecbf778c0c83a5bd62b0f03ca9f49a618 upstream. + +When removing a rule that jumps to chain and such chain in the same +batch, this bogusly hits EBUSY. Add activate and deactivate operations +to expression that can be called from the preparation and the +commit/abort phases. + +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + include/net/netfilter/nf_tables.h | 5 ++++ + net/netfilter/nf_tables_api.c | 46 ++++++++++++++++++++++++++++++++++---- + net/netfilter/nft_immediate.c | 15 +++++++++--- + 3 files changed, 59 insertions(+), 7 deletions(-) + +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -177,6 +177,7 @@ struct nft_data_desc { + int nft_data_init(const struct nft_ctx *ctx, + struct nft_data *data, unsigned int size, + struct nft_data_desc *desc, const struct nlattr *nla); ++void nft_data_hold(const struct nft_data *data, enum nft_data_types type); + void nft_data_release(const struct nft_data *data, enum nft_data_types type); + int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data, + enum nft_data_types type, unsigned int len); +@@ -731,6 +732,10 @@ struct nft_expr_ops { + int (*init)(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]); ++ void (*activate)(const struct nft_ctx *ctx, ++ const struct nft_expr *expr); ++ void (*deactivate)(const struct nft_ctx *ctx, ++ const struct nft_expr *expr); + void (*destroy)(const struct nft_ctx *ctx, + const struct nft_expr *expr); + int (*dump)(struct sk_buff *skb, +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -220,6 +220,34 @@ static int nft_delchain(struct nft_ctx * + return err; + } + ++static void nft_rule_expr_activate(const struct nft_ctx *ctx, ++ struct nft_rule *rule) ++{ ++ struct nft_expr *expr; ++ ++ expr = nft_expr_first(rule); ++ while (expr != nft_expr_last(rule) && expr->ops) { ++ if (expr->ops->activate) ++ expr->ops->activate(ctx, expr); ++ ++ expr = nft_expr_next(expr); ++ } ++} ++ ++static void nft_rule_expr_deactivate(const struct nft_ctx *ctx, ++ struct nft_rule *rule) ++{ ++ struct nft_expr *expr; ++ ++ expr = nft_expr_first(rule); ++ while (expr != nft_expr_last(rule) && expr->ops) { ++ if (expr->ops->deactivate) ++ expr->ops->deactivate(ctx, expr); ++ ++ expr = nft_expr_next(expr); ++ } ++} ++ + static int + nf_tables_delrule_deactivate(struct nft_ctx *ctx, struct nft_rule *rule) + { +@@ -265,6 +293,7 @@ static int nft_delrule(struct nft_ctx *c + nft_trans_destroy(trans); + return err; + } ++ nft_rule_expr_deactivate(ctx, rule); + + return 0; + } +@@ -2218,6 +2247,13 @@ static void nf_tables_rule_destroy(const + kfree(rule); + } + ++static void nf_tables_rule_release(const struct nft_ctx *ctx, ++ struct nft_rule *rule) ++{ ++ nft_rule_expr_deactivate(ctx, rule); ++ nf_tables_rule_destroy(ctx, rule); ++} ++ + #define NFT_RULE_MAXEXPRS 128 + + static struct nft_expr_info *info; +@@ -2385,7 +2421,7 @@ static int nf_tables_newrule(struct net + return 0; + + err2: +- nf_tables_rule_destroy(&ctx, rule); ++ nf_tables_rule_release(&ctx, rule); + err1: + for (i = 0; i < n; i++) { + if (info[i].ops != NULL) +@@ -4054,7 +4090,7 @@ static int nf_tables_newsetelem(struct n + * NFT_GOTO verdicts. This function must be called on active data objects + * from the second phase of the commit protocol. + */ +-static void nft_data_hold(const struct nft_data *data, enum nft_data_types type) ++void nft_data_hold(const struct nft_data *data, enum nft_data_types type) + { + if (type == NFT_DATA_VERDICT) { + switch (data->verdict.code) { +@@ -5221,10 +5257,12 @@ static int nf_tables_abort(struct net *n + case NFT_MSG_NEWRULE: + trans->ctx.chain->use--; + list_del_rcu(&nft_trans_rule(trans)->list); ++ nft_rule_expr_deactivate(&trans->ctx, nft_trans_rule(trans)); + break; + case NFT_MSG_DELRULE: + trans->ctx.chain->use++; + nft_clear(trans->ctx.net, nft_trans_rule(trans)); ++ nft_rule_expr_activate(&trans->ctx, nft_trans_rule(trans)); + nft_trans_destroy(trans); + break; + case NFT_MSG_NEWSET: +@@ -5798,7 +5836,7 @@ int __nft_release_basechain(struct nft_c + list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) { + list_del(&rule->list); + ctx->chain->use--; +- nf_tables_rule_destroy(ctx, rule); ++ nf_tables_rule_release(ctx, rule); + } + list_del(&ctx->chain->list); + ctx->table->use--; +@@ -5832,7 +5870,7 @@ static void __nft_release_afinfo(struct + list_for_each_entry_safe(rule, nr, &chain->rules, list) { + list_del(&rule->list); + chain->use--; +- nf_tables_rule_destroy(&ctx, rule); ++ nf_tables_rule_release(&ctx, rule); + } + } + list_for_each_entry_safe(set, ns, &table->sets, list) { +--- a/net/netfilter/nft_immediate.c ++++ b/net/netfilter/nft_immediate.c +@@ -69,8 +69,16 @@ err1: + return err; + } + +-static void nft_immediate_destroy(const struct nft_ctx *ctx, +- const struct nft_expr *expr) ++static void nft_immediate_activate(const struct nft_ctx *ctx, ++ const struct nft_expr *expr) ++{ ++ const struct nft_immediate_expr *priv = nft_expr_priv(expr); ++ ++ return nft_data_hold(&priv->data, nft_dreg_to_type(priv->dreg)); ++} ++ ++static void nft_immediate_deactivate(const struct nft_ctx *ctx, ++ const struct nft_expr *expr) + { + const struct nft_immediate_expr *priv = nft_expr_priv(expr); + +@@ -108,7 +116,8 @@ static const struct nft_expr_ops nft_imm + .size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)), + .eval = nft_immediate_eval, + .init = nft_immediate_init, +- .destroy = nft_immediate_destroy, ++ .activate = nft_immediate_activate, ++ .deactivate = nft_immediate_deactivate, + .dump = nft_immediate_dump, + .validate = nft_immediate_validate, + }; diff --git a/queue-4.14/netfilter-nf_tables-disable-preemption-in-nft_update_chain_stats.patch b/queue-4.14/netfilter-nf_tables-disable-preemption-in-nft_update_chain_stats.patch new file mode 100644 index 00000000000..f795295f9a6 --- /dev/null +++ b/queue-4.14/netfilter-nf_tables-disable-preemption-in-nft_update_chain_stats.patch @@ -0,0 +1,46 @@ +From ad9d9e85072b668731f356be0a3750a3ba22a607 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Sun, 27 May 2018 21:08:13 +0200 +Subject: netfilter: nf_tables: disable preemption in nft_update_chain_stats() + +From: Pablo Neira Ayuso + +commit ad9d9e85072b668731f356be0a3750a3ba22a607 upstream. + +This patch fixes the following splat. + +[118709.054937] BUG: using smp_processor_id() in preemptible [00000000] code: test/1571 +[118709.054970] caller is nft_update_chain_stats.isra.4+0x53/0x97 [nf_tables] +[118709.054980] CPU: 2 PID: 1571 Comm: test Not tainted 4.17.0-rc6+ #335 +[...] +[118709.054992] Call Trace: +[118709.055011] dump_stack+0x5f/0x86 +[118709.055026] check_preemption_disabled+0xd4/0xe4 + +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/netfilter/nf_tables_core.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/net/netfilter/nf_tables_core.c ++++ b/net/netfilter/nf_tables_core.c +@@ -126,15 +126,15 @@ static noinline void nft_update_chain_st + if (!base_chain->stats) + return; + ++ local_bh_disable(); + stats = this_cpu_ptr(rcu_dereference(base_chain->stats)); + if (stats) { +- local_bh_disable(); + u64_stats_update_begin(&stats->syncp); + stats->pkts++; + stats->bytes += pkt->skb->len; + u64_stats_update_end(&stats->syncp); +- local_bh_enable(); + } ++ local_bh_enable(); + } + + struct nft_jumpstack { diff --git a/queue-4.14/netfilter-nf_tables-don-t-assume-chain-stats-are-set-when-jumplabel-is-set.patch b/queue-4.14/netfilter-nf_tables-don-t-assume-chain-stats-are-set-when-jumplabel-is-set.patch new file mode 100644 index 00000000000..1da9047552d --- /dev/null +++ b/queue-4.14/netfilter-nf_tables-don-t-assume-chain-stats-are-set-when-jumplabel-is-set.patch @@ -0,0 +1,54 @@ +From 009240940e84c1c089af88b454f7e804a4c5bd1b Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Sun, 6 May 2018 00:47:20 +0200 +Subject: netfilter: nf_tables: don't assume chain stats are set when jumplabel is set + +From: Florian Westphal + +commit 009240940e84c1c089af88b454f7e804a4c5bd1b upstream. + +nft_chain_stats_replace() and all other spots assume ->stats can be +NULL, but nft_update_chain_stats does not. It must do this check, +just because the jump label is set doesn't mean all basechains have stats +assigned. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/netfilter/nf_tables_core.c | 21 ++++++++++++++------- + 1 file changed, 14 insertions(+), 7 deletions(-) + +--- a/net/netfilter/nf_tables_core.c ++++ b/net/netfilter/nf_tables_core.c +@@ -119,15 +119,22 @@ DEFINE_STATIC_KEY_FALSE(nft_counters_ena + static noinline void nft_update_chain_stats(const struct nft_chain *chain, + const struct nft_pktinfo *pkt) + { ++ struct nft_base_chain *base_chain; + struct nft_stats *stats; + +- local_bh_disable(); +- stats = this_cpu_ptr(rcu_dereference(nft_base_chain(chain)->stats)); +- u64_stats_update_begin(&stats->syncp); +- stats->pkts++; +- stats->bytes += pkt->skb->len; +- u64_stats_update_end(&stats->syncp); +- local_bh_enable(); ++ base_chain = nft_base_chain(chain); ++ if (!base_chain->stats) ++ return; ++ ++ stats = this_cpu_ptr(rcu_dereference(base_chain->stats)); ++ if (stats) { ++ local_bh_disable(); ++ u64_stats_update_begin(&stats->syncp); ++ stats->pkts++; ++ stats->bytes += pkt->skb->len; ++ u64_stats_update_end(&stats->syncp); ++ local_bh_enable(); ++ } + } + + struct nft_jumpstack { diff --git a/queue-4.14/netfilter-nf_tables-fix-memory-leak-on-error-exit-return.patch b/queue-4.14/netfilter-nf_tables-fix-memory-leak-on-error-exit-return.patch new file mode 100644 index 00000000000..bad9f6f4c23 --- /dev/null +++ b/queue-4.14/netfilter-nf_tables-fix-memory-leak-on-error-exit-return.patch @@ -0,0 +1,40 @@ +From f0dfd7a2b35b02030949100247d851b793cb275f Mon Sep 17 00:00:00 2001 +From: Colin Ian King +Date: Wed, 9 May 2018 13:22:56 +0100 +Subject: netfilter: nf_tables: fix memory leak on error exit return + +From: Colin Ian King + +commit f0dfd7a2b35b02030949100247d851b793cb275f upstream. + +Currently the -EBUSY error return path is not free'ing resources +allocated earlier, leaving a memory leak. Fix this by exiting via the +error exit label err5 that performs the necessary resource clean +up. + +Detected by CoverityScan, CID#1432975 ("Resource leak") + +Fixes: 9744a6fcefcb ("netfilter: nf_tables: check if same extensions are set when adding elements") +Signed-off-by: Colin Ian King +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/netfilter/nf_tables_api.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -3999,8 +3999,10 @@ static int nft_add_set_elem(struct nft_c + if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) ^ + nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) || + nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) ^ +- nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF)) +- return -EBUSY; ++ nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF)) { ++ err = -EBUSY; ++ goto err5; ++ } + if ((nft_set_ext_exists(ext, NFT_SET_EXT_DATA) && + nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) && + memcmp(nft_set_ext_data(ext), diff --git a/queue-4.14/netfilter-nf_tables-increase-nft_counters_enabled-in-nft_chain_stats_replace.patch b/queue-4.14/netfilter-nf_tables-increase-nft_counters_enabled-in-nft_chain_stats_replace.patch new file mode 100644 index 00000000000..e0bbdd11a1b --- /dev/null +++ b/queue-4.14/netfilter-nf_tables-increase-nft_counters_enabled-in-nft_chain_stats_replace.patch @@ -0,0 +1,64 @@ +From bbb8c61f97e3a2dd91b30d3e57b7964a67569d11 Mon Sep 17 00:00:00 2001 +From: Taehee Yoo +Date: Tue, 29 May 2018 01:14:12 +0900 +Subject: netfilter: nf_tables: increase nft_counters_enabled in nft_chain_stats_replace() + +From: Taehee Yoo + +commit bbb8c61f97e3a2dd91b30d3e57b7964a67569d11 upstream. + +When a chain is updated, a counter can be attached. if so, +the nft_counters_enabled should be increased. + +test commands: + + %nft add table ip filter + %nft add chain ip filter input { type filter hook input priority 4\; } + %iptables-compat -Z input + %nft delete chain ip filter input + +we can see below messages. + +[ 286.443720] jump label: negative count! +[ 286.448278] WARNING: CPU: 0 PID: 1459 at kernel/jump_label.c:197 __static_key_slow_dec_cpuslocked+0x6f/0xf0 +[ 286.449144] Modules linked in: nf_tables nfnetlink ip_tables x_tables +[ 286.449144] CPU: 0 PID: 1459 Comm: nft Tainted: G W 4.17.0-rc2+ #12 +[ 286.449144] RIP: 0010:__static_key_slow_dec_cpuslocked+0x6f/0xf0 +[ 286.449144] RSP: 0018:ffff88010e5176f0 EFLAGS: 00010286 +[ 286.449144] RAX: 000000000000001b RBX: ffffffffc0179500 RCX: ffffffffb8a82522 +[ 286.449144] RDX: 0000000000000001 RSI: 0000000000000008 RDI: ffff88011b7e5eac +[ 286.449144] RBP: 0000000000000000 R08: ffffed00236fce5c R09: ffffed00236fce5b +[ 286.449144] R10: ffffffffc0179503 R11: ffffed00236fce5c R12: 0000000000000000 +[ 286.449144] R13: ffff88011a28e448 R14: ffff88011a28e470 R15: dffffc0000000000 +[ 286.449144] FS: 00007f0384328700(0000) GS:ffff88011b600000(0000) knlGS:0000000000000000 +[ 286.449144] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 286.449144] CR2: 00007f038394bf10 CR3: 0000000104a86000 CR4: 00000000001006f0 +[ 286.449144] Call Trace: +[ 286.449144] static_key_slow_dec+0x6a/0x70 +[ 286.449144] nf_tables_chain_destroy+0x19d/0x210 [nf_tables] +[ 286.449144] nf_tables_commit+0x1891/0x1c50 [nf_tables] +[ 286.449144] nfnetlink_rcv+0x1148/0x13d0 [nfnetlink] +[ ... ] + +Signed-off-by: Taehee Yoo +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/netfilter/nf_tables_api.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -1266,8 +1266,10 @@ static void nft_chain_stats_replace(stru + rcu_assign_pointer(chain->stats, newstats); + synchronize_rcu(); + free_percpu(oldstats); +- } else ++ } else { + rcu_assign_pointer(chain->stats, newstats); ++ static_branch_inc(&nft_counters_enabled); ++ } + } + + static void nf_tables_chain_destroy(struct nft_chain *chain) diff --git a/queue-4.14/netfilter-nf_tables-nft_compat-fix-refcount-leak-on-xt-module.patch b/queue-4.14/netfilter-nf_tables-nft_compat-fix-refcount-leak-on-xt-module.patch new file mode 100644 index 00000000000..6e03e85f0a8 --- /dev/null +++ b/queue-4.14/netfilter-nf_tables-nft_compat-fix-refcount-leak-on-xt-module.patch @@ -0,0 +1,287 @@ +From b8e9dc1c75714ceb53615743e1036f76e00f5a17 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Wed, 2 May 2018 14:07:42 +0200 +Subject: netfilter: nf_tables: nft_compat: fix refcount leak on xt module + +From: Florian Westphal + +commit b8e9dc1c75714ceb53615743e1036f76e00f5a17 upstream. + +Taehee Yoo reported following bug: + iptables-compat -I OUTPUT -m cpu --cpu 0 + iptables-compat -F + lsmod |grep xt_cpu + xt_cpu 16384 1 + +Quote: +"When above command is given, a netlink message has two expressions that +are the cpu compat and the nft_counter. +The nft_expr_type_get() in the nf_tables_expr_parse() successes +first expression then, calls select_ops callback. +(allocates memory and holds module) +But, second nft_expr_type_get() in the nf_tables_expr_parse() +returns -EAGAIN because of request_module(). +In that point, by the 'goto err1', +the 'module_put(info[i].ops->type->owner)' is called. +There is no release routine." + +The core problem is that unlike all other expression, +nft_compat select_ops has side effects. + +1. it allocates dynamic memory which holds an nft ops struct. + In all other expressions, ops has static storage duration. +2. It grabs references to the xt module that it is supposed to + invoke. + +Depending on where things go wrong, error unwinding doesn't +always do the right thing. + +In the above scenario, a new nft_compat_expr is created and +xt_cpu module gets loaded with a refcount of 1. + +Due to to -EAGAIN, the netlink messages get re-parsed. +When that happens, nft_compat finds that xt_cpu is already present +and increments module refcount again. + +This fixes the problem by making select_ops to have no visible +side effects and removes all extra module_get/put. + +When select_ops creates a new nft_compat expression, the new +expression has a refcount of 0, and the xt module gets its refcount +incremented. + +When error happens, the next call finds existing entry, but will no +longer increase the reference count -- the presence of existing +nft_xt means we already hold a module reference. + +Because nft_xt_put is only called from nft_compat destroy hook, +it will never see the initial zero reference count. +->destroy can only be called after ->init(), and that will increase the +refcount. + +Lastly, we now free nft_xt struct with kfree_rcu. +Else, we get use-after free in nf_tables_rule_destroy: + + while (expr != nft_expr_last(rule) && expr->ops) { + nf_tables_expr_destroy(ctx, expr); + expr = nft_expr_next(expr); // here + +nft_expr_next() dereferences expr->ops. This is safe +for all users, as ops have static storage duration. +In nft_compat case however, its ->destroy callback can +free the memory that hold the ops structure. + +Tested-by: Taehee Yoo +Reported-by: Taehee Yoo +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/netfilter/nft_compat.c | 92 ++++++++++++++++++++++++++++----------------- + 1 file changed, 58 insertions(+), 34 deletions(-) + +--- a/net/netfilter/nft_compat.c ++++ b/net/netfilter/nft_compat.c +@@ -27,14 +27,24 @@ struct nft_xt { + struct list_head head; + struct nft_expr_ops ops; + unsigned int refcnt; ++ ++ /* Unlike other expressions, ops doesn't have static storage duration. ++ * nft core assumes they do. We use kfree_rcu so that nft core can ++ * can check expr->ops->size even after nft_compat->destroy() frees ++ * the nft_xt struct that holds the ops structure. ++ */ ++ struct rcu_head rcu_head; + }; + +-static void nft_xt_put(struct nft_xt *xt) ++static bool nft_xt_put(struct nft_xt *xt) + { + if (--xt->refcnt == 0) { + list_del(&xt->head); +- kfree(xt); ++ kfree_rcu(xt, rcu_head); ++ return true; + } ++ ++ return false; + } + + static int nft_compat_chain_validate_dependency(const char *tablename, +@@ -226,6 +236,7 @@ nft_target_init(const struct nft_ctx *ct + struct xt_target *target = expr->ops->data; + struct xt_tgchk_param par; + size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO])); ++ struct nft_xt *nft_xt; + u16 proto = 0; + bool inv = false; + union nft_entry e = {}; +@@ -236,25 +247,22 @@ nft_target_init(const struct nft_ctx *ct + if (ctx->nla[NFTA_RULE_COMPAT]) { + ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv); + if (ret < 0) +- goto err; ++ return ret; + } + + nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv); + + ret = xt_check_target(&par, size, proto, inv); + if (ret < 0) +- goto err; ++ return ret; + + /* The standard target cannot be used */ +- if (target->target == NULL) { +- ret = -EINVAL; +- goto err; +- } ++ if (!target->target) ++ return -EINVAL; + ++ nft_xt = container_of(expr->ops, struct nft_xt, ops); ++ nft_xt->refcnt++; + return 0; +-err: +- module_put(target->me); +- return ret; + } + + static void +@@ -271,8 +279,8 @@ nft_target_destroy(const struct nft_ctx + if (par.target->destroy != NULL) + par.target->destroy(&par); + +- nft_xt_put(container_of(expr->ops, struct nft_xt, ops)); +- module_put(target->me); ++ if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops))) ++ module_put(target->me); + } + + static int nft_target_dump(struct sk_buff *skb, const struct nft_expr *expr) +@@ -411,6 +419,7 @@ nft_match_init(const struct nft_ctx *ctx + struct xt_match *match = expr->ops->data; + struct xt_mtchk_param par; + size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO])); ++ struct nft_xt *nft_xt; + u16 proto = 0; + bool inv = false; + union nft_entry e = {}; +@@ -421,19 +430,18 @@ nft_match_init(const struct nft_ctx *ctx + if (ctx->nla[NFTA_RULE_COMPAT]) { + ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv); + if (ret < 0) +- goto err; ++ return ret; + } + + nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv); + + ret = xt_check_match(&par, size, proto, inv); + if (ret < 0) +- goto err; ++ return ret; + ++ nft_xt = container_of(expr->ops, struct nft_xt, ops); ++ nft_xt->refcnt++; + return 0; +-err: +- module_put(match->me); +- return ret; + } + + static void +@@ -450,8 +458,8 @@ nft_match_destroy(const struct nft_ctx * + if (par.match->destroy != NULL) + par.match->destroy(&par); + +- nft_xt_put(container_of(expr->ops, struct nft_xt, ops)); +- module_put(match->me); ++ if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops))) ++ module_put(match->me); + } + + static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr) +@@ -654,13 +662,8 @@ nft_match_select_ops(const struct nft_ct + list_for_each_entry(nft_match, &nft_match_list, head) { + struct xt_match *match = nft_match->ops.data; + +- if (nft_match_cmp(match, mt_name, rev, family)) { +- if (!try_module_get(match->me)) +- return ERR_PTR(-ENOENT); +- +- nft_match->refcnt++; ++ if (nft_match_cmp(match, mt_name, rev, family)) + return &nft_match->ops; +- } + } + + match = xt_request_find_match(family, mt_name, rev); +@@ -679,7 +682,7 @@ nft_match_select_ops(const struct nft_ct + goto err; + } + +- nft_match->refcnt = 1; ++ nft_match->refcnt = 0; + nft_match->ops.type = &nft_match_type; + nft_match->ops.size = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize)); + nft_match->ops.eval = nft_match_eval; +@@ -739,13 +742,8 @@ nft_target_select_ops(const struct nft_c + list_for_each_entry(nft_target, &nft_target_list, head) { + struct xt_target *target = nft_target->ops.data; + +- if (nft_target_cmp(target, tg_name, rev, family)) { +- if (!try_module_get(target->me)) +- return ERR_PTR(-ENOENT); +- +- nft_target->refcnt++; ++ if (nft_target_cmp(target, tg_name, rev, family)) + return &nft_target->ops; +- } + } + + target = xt_request_find_target(family, tg_name, rev); +@@ -764,7 +762,7 @@ nft_target_select_ops(const struct nft_c + goto err; + } + +- nft_target->refcnt = 1; ++ nft_target->refcnt = 0; + nft_target->ops.type = &nft_target_type; + nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize)); + nft_target->ops.init = nft_target_init; +@@ -825,6 +823,32 @@ err_match: + + static void __exit nft_compat_module_exit(void) + { ++ struct nft_xt *xt, *next; ++ ++ /* list should be empty here, it can be non-empty only in case there ++ * was an error that caused nft_xt expr to not be initialized fully ++ * and noone else requested the same expression later. ++ * ++ * In this case, the lists contain 0-refcount entries that still ++ * hold module reference. ++ */ ++ list_for_each_entry_safe(xt, next, &nft_target_list, head) { ++ struct xt_target *target = xt->ops.data; ++ ++ if (WARN_ON_ONCE(xt->refcnt)) ++ continue; ++ module_put(target->me); ++ kfree(xt); ++ } ++ ++ list_for_each_entry_safe(xt, next, &nft_match_list, head) { ++ struct xt_match *match = xt->ops.data; ++ ++ if (WARN_ON_ONCE(xt->refcnt)) ++ continue; ++ module_put(match->me); ++ kfree(xt); ++ } + nfnetlink_subsys_unregister(&nfnl_compat_subsys); + nft_unregister_expr(&nft_target_type); + nft_unregister_expr(&nft_match_type); diff --git a/queue-4.14/netfilter-nft_compat-fix-handling-of-large-matchinfo-size.patch b/queue-4.14/netfilter-nft_compat-fix-handling-of-large-matchinfo-size.patch new file mode 100644 index 00000000000..1cc2821b72e --- /dev/null +++ b/queue-4.14/netfilter-nft_compat-fix-handling-of-large-matchinfo-size.patch @@ -0,0 +1,151 @@ +From 732a8049f365f514d0607e03938491bf6cb0d620 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Mon, 7 May 2018 15:22:36 +0200 +Subject: netfilter: nft_compat: fix handling of large matchinfo size + +From: Florian Westphal + +commit 732a8049f365f514d0607e03938491bf6cb0d620 upstream. + +currently matchinfo gets stored in the expression, but some xt matches +are very large. + +To handle those we either need to switch nft core to kvmalloc and increase +size limit, or allocate the info blob of large matches separately. + +This does the latter, this limits the scope of the changes to +nft_compat. + +I picked a threshold of 192, this allows most matches to work as before and +handle only few ones via separate alloation (cgroup, u32, sctp, rt). + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/netfilter/nft_compat.c | 64 ++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 63 insertions(+), 1 deletion(-) + +--- a/net/netfilter/nft_compat.c ++++ b/net/netfilter/nft_compat.c +@@ -36,6 +36,13 @@ struct nft_xt { + struct rcu_head rcu_head; + }; + ++/* Used for matches where *info is larger than X byte */ ++#define NFT_MATCH_LARGE_THRESH 192 ++ ++struct nft_xt_match_priv { ++ void *info; ++}; ++ + static bool nft_xt_put(struct nft_xt *xt) + { + if (--xt->refcnt == 0) { +@@ -352,6 +359,15 @@ static void __nft_match_eval(const struc + } + } + ++static void nft_match_large_eval(const struct nft_expr *expr, ++ struct nft_regs *regs, ++ const struct nft_pktinfo *pkt) ++{ ++ struct nft_xt_match_priv *priv = nft_expr_priv(expr); ++ ++ __nft_match_eval(expr, regs, pkt, priv->info); ++} ++ + static void nft_match_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +@@ -458,6 +474,24 @@ nft_match_init(const struct nft_ctx *ctx + return __nft_match_init(ctx, expr, tb, nft_expr_priv(expr)); + } + ++static int ++nft_match_large_init(const struct nft_ctx *ctx, const struct nft_expr *expr, ++ const struct nlattr * const tb[]) ++{ ++ struct nft_xt_match_priv *priv = nft_expr_priv(expr); ++ struct xt_match *m = expr->ops->data; ++ int ret; ++ ++ priv->info = kmalloc(XT_ALIGN(m->matchsize), GFP_KERNEL); ++ if (!priv->info) ++ return -ENOMEM; ++ ++ ret = __nft_match_init(ctx, expr, tb, priv->info); ++ if (ret) ++ kfree(priv->info); ++ return ret; ++} ++ + static void + __nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr, + void *info) +@@ -482,6 +516,15 @@ nft_match_destroy(const struct nft_ctx * + __nft_match_destroy(ctx, expr, nft_expr_priv(expr)); + } + ++static void ++nft_match_large_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) ++{ ++ struct nft_xt_match_priv *priv = nft_expr_priv(expr); ++ ++ __nft_match_destroy(ctx, expr, priv->info); ++ kfree(priv->info); ++} ++ + static int __nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr, + void *info) + { +@@ -503,6 +546,13 @@ static int nft_match_dump(struct sk_buff + return __nft_match_dump(skb, expr, nft_expr_priv(expr)); + } + ++static int nft_match_large_dump(struct sk_buff *skb, const struct nft_expr *e) ++{ ++ struct nft_xt_match_priv *priv = nft_expr_priv(e); ++ ++ return __nft_match_dump(skb, e, priv->info); ++} ++ + static int nft_match_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nft_data **data) +@@ -670,6 +720,7 @@ nft_match_select_ops(const struct nft_ct + { + struct nft_xt *nft_match; + struct xt_match *match; ++ unsigned int matchsize; + char *mt_name; + u32 rev, family; + int err; +@@ -709,7 +760,6 @@ nft_match_select_ops(const struct nft_ct + + nft_match->refcnt = 0; + nft_match->ops.type = &nft_match_type; +- nft_match->ops.size = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize)); + nft_match->ops.eval = nft_match_eval; + nft_match->ops.init = nft_match_init; + nft_match->ops.destroy = nft_match_destroy; +@@ -717,6 +767,18 @@ nft_match_select_ops(const struct nft_ct + nft_match->ops.validate = nft_match_validate; + nft_match->ops.data = match; + ++ matchsize = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize)); ++ if (matchsize > NFT_MATCH_LARGE_THRESH) { ++ matchsize = NFT_EXPR_SIZE(sizeof(struct nft_xt_match_priv)); ++ ++ nft_match->ops.eval = nft_match_large_eval; ++ nft_match->ops.init = nft_match_large_init; ++ nft_match->ops.destroy = nft_match_large_destroy; ++ nft_match->ops.dump = nft_match_large_dump; ++ } ++ ++ nft_match->ops.size = matchsize; ++ + list_add(&nft_match->head, &nft_match_list); + + return &nft_match->ops; diff --git a/queue-4.14/netfilter-nft_compat-prepare-for-indirect-info-storage.patch b/queue-4.14/netfilter-nft_compat-prepare-for-indirect-info-storage.patch new file mode 100644 index 00000000000..7aee4934512 --- /dev/null +++ b/queue-4.14/netfilter-nft_compat-prepare-for-indirect-info-storage.patch @@ -0,0 +1,120 @@ +From 8bdf164744b2c7f63561846c01cff3db597f282d Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Mon, 7 May 2018 15:22:35 +0200 +Subject: netfilter: nft_compat: prepare for indirect info storage + +From: Florian Westphal + +commit 8bdf164744b2c7f63561846c01cff3db597f282d upstream. + +Next patch will make it possible for *info to be stored in +a separate allocation instead of the expr private area. + +This removes the 'expr priv area is info blob' assumption +from the match init/destroy/eval functions. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/netfilter/nft_compat.c | 47 ++++++++++++++++++++++++++++++++++----------- + 1 file changed, 36 insertions(+), 11 deletions(-) + +--- a/net/netfilter/nft_compat.c ++++ b/net/netfilter/nft_compat.c +@@ -324,11 +324,11 @@ static int nft_target_validate(const str + return 0; + } + +-static void nft_match_eval(const struct nft_expr *expr, +- struct nft_regs *regs, +- const struct nft_pktinfo *pkt) ++static void __nft_match_eval(const struct nft_expr *expr, ++ struct nft_regs *regs, ++ const struct nft_pktinfo *pkt, ++ void *info) + { +- void *info = nft_expr_priv(expr); + struct xt_match *match = expr->ops->data; + struct sk_buff *skb = pkt->skb; + bool ret; +@@ -352,6 +352,13 @@ static void nft_match_eval(const struct + } + } + ++static void nft_match_eval(const struct nft_expr *expr, ++ struct nft_regs *regs, ++ const struct nft_pktinfo *pkt) ++{ ++ __nft_match_eval(expr, regs, pkt, nft_expr_priv(expr)); ++} ++ + static const struct nla_policy nft_match_policy[NFTA_MATCH_MAX + 1] = { + [NFTA_MATCH_NAME] = { .type = NLA_NUL_STRING }, + [NFTA_MATCH_REV] = { .type = NLA_U32 }, +@@ -412,10 +419,10 @@ static void match_compat_from_user(struc + } + + static int +-nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, +- const struct nlattr * const tb[]) ++__nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, ++ const struct nlattr * const tb[], ++ void *info) + { +- void *info = nft_expr_priv(expr); + struct xt_match *match = expr->ops->data; + struct xt_mtchk_param par; + size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO])); +@@ -444,11 +451,18 @@ nft_match_init(const struct nft_ctx *ctx + return 0; + } + ++static int ++nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, ++ const struct nlattr * const tb[]) ++{ ++ return __nft_match_init(ctx, expr, tb, nft_expr_priv(expr)); ++} ++ + static void +-nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) ++__nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr, ++ void *info) + { + struct xt_match *match = expr->ops->data; +- void *info = nft_expr_priv(expr); + struct xt_mtdtor_param par; + + par.net = ctx->net; +@@ -462,9 +476,15 @@ nft_match_destroy(const struct nft_ctx * + module_put(match->me); + } + +-static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr) ++static void ++nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) ++{ ++ __nft_match_destroy(ctx, expr, nft_expr_priv(expr)); ++} ++ ++static int __nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr, ++ void *info) + { +- void *info = nft_expr_priv(expr); + struct xt_match *match = expr->ops->data; + + if (nla_put_string(skb, NFTA_MATCH_NAME, match->name) || +@@ -478,6 +498,11 @@ nla_put_failure: + return -1; + } + ++static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr) ++{ ++ return __nft_match_dump(skb, expr, nft_expr_priv(expr)); ++} ++ + static int nft_match_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nft_data **data) diff --git a/queue-4.14/netfilter-nft_meta-fix-wrong-value-dereference-in-nft_meta_set_eval.patch b/queue-4.14/netfilter-nft_meta-fix-wrong-value-dereference-in-nft_meta_set_eval.patch new file mode 100644 index 00000000000..a5ce8f30901 --- /dev/null +++ b/queue-4.14/netfilter-nft_meta-fix-wrong-value-dereference-in-nft_meta_set_eval.patch @@ -0,0 +1,71 @@ +From 97a0549b15a0b466c47f6a0143a490a082c64b4e Mon Sep 17 00:00:00 2001 +From: Taehee Yoo +Date: Thu, 17 May 2018 22:49:49 +0900 +Subject: netfilter: nft_meta: fix wrong value dereference in nft_meta_set_eval + +From: Taehee Yoo + +commit 97a0549b15a0b466c47f6a0143a490a082c64b4e upstream. + +In the nft_meta_set_eval, nftrace value is dereferenced as u32 from sreg. +But correct type is u8. so that sometimes incorrect value is dereferenced. + +Steps to reproduce: + + %nft add table ip filter + %nft add chain ip filter input { type filter hook input priority 4\; } + %nft add rule ip filter input nftrace set 0 + %nft monitor + +Sometimes, we can see trace messages. + + trace id 16767227 ip filter input packet: iif "enp2s0" + ether saddr xx:xx:xx:xx:xx:xx ether daddr xx:xx:xx:xx:xx:xx + ip saddr 192.168.0.1 ip daddr 255.255.255.255 ip dscp cs0 + ip ecn not-ect ip + trace id 16767227 ip filter input rule nftrace set 0 (verdict continue) + trace id 16767227 ip filter input verdict continue + trace id 16767227 ip filter input + +Signed-off-by: Taehee Yoo +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/netfilter/nft_meta.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +--- a/net/netfilter/nft_meta.c ++++ b/net/netfilter/nft_meta.c +@@ -229,7 +229,7 @@ void nft_meta_set_eval(const struct nft_ + struct sk_buff *skb = pkt->skb; + u32 *sreg = ®s->data[meta->sreg]; + u32 value = *sreg; +- u8 pkt_type; ++ u8 value8; + + switch (meta->key) { + case NFT_META_MARK: +@@ -239,15 +239,17 @@ void nft_meta_set_eval(const struct nft_ + skb->priority = value; + break; + case NFT_META_PKTTYPE: +- pkt_type = nft_reg_load8(sreg); ++ value8 = nft_reg_load8(sreg); + +- if (skb->pkt_type != pkt_type && +- skb_pkt_type_ok(pkt_type) && ++ if (skb->pkt_type != value8 && ++ skb_pkt_type_ok(value8) && + skb_pkt_type_ok(skb->pkt_type)) +- skb->pkt_type = pkt_type; ++ skb->pkt_type = value8; + break; + case NFT_META_NFTRACE: +- skb->nf_trace = !!value; ++ value8 = nft_reg_load8(sreg); ++ ++ skb->nf_trace = !!value8; + break; + default: + WARN_ON(1); diff --git a/queue-4.14/series b/queue-4.14/series index d8be30a3135..59899470ba2 100644 --- a/queue-4.14/series +++ b/queue-4.14/series @@ -21,3 +21,13 @@ drm-amdgpu-use-kvmalloc_array-for-allocating-vram-manager-nodes-array.patch drm-amdgpu-refactor-amdgpu_vram_mgr_bo_invisible_size-helper.patch drm-amdgpu-make-amdgpu_vram_mgr_bo_invisible_size-always-accurate.patch drm-i915-enable-provoking-vertex-fix-on-gen9-systems.patch +netfilter-nf_tables-nft_compat-fix-refcount-leak-on-xt-module.patch +netfilter-nft_compat-prepare-for-indirect-info-storage.patch +netfilter-nft_compat-fix-handling-of-large-matchinfo-size.patch +netfilter-nf_tables-don-t-assume-chain-stats-are-set-when-jumplabel-is-set.patch +netfilter-nf_tables-bogus-ebusy-in-chain-deletions.patch +netfilter-nft_meta-fix-wrong-value-dereference-in-nft_meta_set_eval.patch +netfilter-nf_tables-disable-preemption-in-nft_update_chain_stats.patch +netfilter-nf_tables-increase-nft_counters_enabled-in-nft_chain_stats_replace.patch +netfilter-nf_tables-fix-memory-leak-on-error-exit-return.patch +netfilter-nf_tables-add-missing-netlink-attrs-to-policies.patch