]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.19-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 13 Aug 2022 15:21:57 +0000 (17:21 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 13 Aug 2022 15:21:57 +0000 (17:21 +0200)
added patches:
epoll-autoremove-wakers-even-more-aggressively.patch
netfilter-nf_tables-disallow-jump-to-implicit-chain-from-set-element.patch
netfilter-nf_tables-do-not-allow-chain_id-to-refer-to-another-table.patch
netfilter-nf_tables-do-not-allow-rule_id-to-refer-to-another-chain.patch
netfilter-nf_tables-do-not-allow-set_id-to-refer-to-another-table.patch
netfilter-nf_tables-fix-null-deref-due-to-zeroed-list-head.patch
netfilter-nf_tables-upfront-validation-of-data-via-nft_data_init.patch

queue-5.19/epoll-autoremove-wakers-even-more-aggressively.patch [new file with mode: 0644]
queue-5.19/netfilter-nf_tables-disallow-jump-to-implicit-chain-from-set-element.patch [new file with mode: 0644]
queue-5.19/netfilter-nf_tables-do-not-allow-chain_id-to-refer-to-another-table.patch [new file with mode: 0644]
queue-5.19/netfilter-nf_tables-do-not-allow-rule_id-to-refer-to-another-chain.patch [new file with mode: 0644]
queue-5.19/netfilter-nf_tables-do-not-allow-set_id-to-refer-to-another-table.patch [new file with mode: 0644]
queue-5.19/netfilter-nf_tables-fix-null-deref-due-to-zeroed-list-head.patch [new file with mode: 0644]
queue-5.19/netfilter-nf_tables-upfront-validation-of-data-via-nft_data_init.patch [new file with mode: 0644]
queue-5.19/series

diff --git a/queue-5.19/epoll-autoremove-wakers-even-more-aggressively.patch b/queue-5.19/epoll-autoremove-wakers-even-more-aggressively.patch
new file mode 100644 (file)
index 0000000..523effa
--- /dev/null
@@ -0,0 +1,90 @@
+From a16ceb13961068f7209e34d7984f8e42d2c06159 Mon Sep 17 00:00:00 2001
+From: Benjamin Segall <bsegall@google.com>
+Date: Wed, 15 Jun 2022 14:24:23 -0700
+Subject: epoll: autoremove wakers even more aggressively
+
+From: Benjamin Segall <bsegall@google.com>
+
+commit a16ceb13961068f7209e34d7984f8e42d2c06159 upstream.
+
+If a process is killed or otherwise exits while having active network
+connections and many threads waiting on epoll_wait, the threads will all
+be woken immediately, but not removed from ep->wq.  Then when network
+traffic scans ep->wq in wake_up, every wakeup attempt will fail, and will
+not remove the entries from the list.
+
+This means that the cost of the wakeup attempt is far higher than usual,
+does not decrease, and this also competes with the dying threads trying to
+actually make progress and remove themselves from the wq.
+
+Handle this by removing visited epoll wq entries unconditionally, rather
+than only when the wakeup succeeds - the structure of ep_poll means that
+the only potential loss is the timed_out->eavail heuristic, which now can
+race and result in a redundant ep_send_events attempt.  (But only when
+incoming data and a timeout actually race, not on every timeout)
+
+Shakeel added:
+
+: We are seeing this issue in production with real workloads and it has
+: caused hard lockups.  Particularly network heavy workloads with a lot
+: of threads in epoll_wait() can easily trigger this issue if they get
+: killed (oom-killed in our case).
+
+Link: https://lkml.kernel.org/r/xm26fsjotqda.fsf@google.com
+Signed-off-by: Ben Segall <bsegall@google.com>
+Tested-by: Shakeel Butt <shakeelb@google.com>
+Cc: Alexander Viro <viro@zeniv.linux.org.uk>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Shakeel Butt <shakeelb@google.com>
+Cc: Eric Dumazet <edumazet@google.com>
+Cc: Roman Penyaev <rpenyaev@suse.de>
+Cc: Jason Baron <jbaron@akamai.com>
+Cc: Khazhismel Kumykov <khazhy@google.com>
+Cc: Heiher <r@hev.cc>
+Cc: <stable@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/eventpoll.c |   22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+--- a/fs/eventpoll.c
++++ b/fs/eventpoll.c
+@@ -1747,6 +1747,21 @@ static struct timespec64 *ep_timeout_to_
+       return to;
+ }
++/*
++ * autoremove_wake_function, but remove even on failure to wake up, because we
++ * know that default_wake_function/ttwu will only fail if the thread is already
++ * woken, and in that case the ep_poll loop will remove the entry anyways, not
++ * try to reuse it.
++ */
++static int ep_autoremove_wake_function(struct wait_queue_entry *wq_entry,
++                                     unsigned int mode, int sync, void *key)
++{
++      int ret = default_wake_function(wq_entry, mode, sync, key);
++
++      list_del_init(&wq_entry->entry);
++      return ret;
++}
++
+ /**
+  * ep_poll - Retrieves ready events, and delivers them to the caller-supplied
+  *           event buffer.
+@@ -1828,8 +1843,15 @@ static int ep_poll(struct eventpoll *ep,
+                * normal wakeup path no need to call __remove_wait_queue()
+                * explicitly, thus ep->lock is not taken, which halts the
+                * event delivery.
++               *
++               * In fact, we now use an even more aggressive function that
++               * unconditionally removes, because we don't reuse the wait
++               * entry between loop iterations. This lets us also avoid the
++               * performance issue if a process is killed, causing all of its
++               * threads to wake up without being removed normally.
+                */
+               init_wait(&wait);
++              wait.func = ep_autoremove_wake_function;
+               write_lock_irq(&ep->lock);
+               /*
diff --git a/queue-5.19/netfilter-nf_tables-disallow-jump-to-implicit-chain-from-set-element.patch b/queue-5.19/netfilter-nf_tables-disallow-jump-to-implicit-chain-from-set-element.patch
new file mode 100644 (file)
index 0000000..974a03a
--- /dev/null
@@ -0,0 +1,61 @@
+From f323ef3a0d49e147365284bc1f02212e617b7f09 Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Mon, 8 Aug 2022 19:30:07 +0200
+Subject: netfilter: nf_tables: disallow jump to implicit chain from set element
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+commit f323ef3a0d49e147365284bc1f02212e617b7f09 upstream.
+
+Extend struct nft_data_desc to add a flag field that specifies
+nft_data_init() is being called for set element data.
+
+Use it to disallow jump to implicit chain from set element, only jump
+to chain via immediate expression is allowed.
+
+Fixes: d0e2c7de92c7 ("netfilter: nf_tables: add NFT_CHAIN_BINDING")
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/netfilter/nf_tables.h |    5 +++++
+ net/netfilter/nf_tables_api.c     |    4 ++++
+ 2 files changed, 9 insertions(+)
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -206,10 +206,15 @@ struct nft_ctx {
+       bool                            report;
+ };
++enum nft_data_desc_flags {
++      NFT_DATA_DESC_SETELEM   = (1 << 0),
++};
++
+ struct nft_data_desc {
+       enum nft_data_types             type;
+       unsigned int                    size;
+       unsigned int                    len;
++      unsigned int                    flags;
+ };
+ int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -5226,6 +5226,7 @@ static int nft_setelem_parse_data(struct
+       desc->type = dtype;
+       desc->size = NFT_DATA_VALUE_MAXLEN;
+       desc->len = set->dlen;
++      desc->flags = NFT_DATA_DESC_SETELEM;
+       return nft_data_init(ctx, data, desc, attr);
+ }
+@@ -9611,6 +9612,9 @@ static int nft_verdict_init(const struct
+                       return PTR_ERR(chain);
+               if (nft_is_base_chain(chain))
+                       return -EOPNOTSUPP;
++              if (desc->flags & NFT_DATA_DESC_SETELEM &&
++                  chain->flags & NFT_CHAIN_BINDING)
++                      return -EINVAL;
+               chain->use++;
+               data->verdict.chain = chain;
diff --git a/queue-5.19/netfilter-nf_tables-do-not-allow-chain_id-to-refer-to-another-table.patch b/queue-5.19/netfilter-nf_tables-do-not-allow-chain_id-to-refer-to-another-table.patch
new file mode 100644 (file)
index 0000000..289a142
--- /dev/null
@@ -0,0 +1,66 @@
+From 95f466d22364a33d183509629d0879885b4f547e Mon Sep 17 00:00:00 2001
+From: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
+Date: Tue, 9 Aug 2022 14:01:47 -0300
+Subject: netfilter: nf_tables: do not allow CHAIN_ID to refer to another table
+
+From: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
+
+commit 95f466d22364a33d183509629d0879885b4f547e upstream.
+
+When doing lookups for chains on the same batch by using its ID, a chain
+from a different table can be used. If a rule is added to a table but
+refers to a chain in a different table, it will be linked to the chain in
+table2, but would have expressions referring to objects in table1.
+
+Then, when table1 is removed, the rule will not be removed as its linked to
+a chain in table2. When expressions in the rule are processed or removed,
+that will lead to a use-after-free.
+
+When looking for chains by ID, use the table that was used for the lookup
+by name, and only return chains belonging to that same table.
+
+Fixes: 837830a4b439 ("netfilter: nf_tables: add NFTA_RULE_CHAIN_ID attribute")
+Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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
+@@ -2472,6 +2472,7 @@ err:
+ }
+ static struct nft_chain *nft_chain_lookup_byid(const struct net *net,
++                                             const struct nft_table *table,
+                                              const struct nlattr *nla)
+ {
+       struct nftables_pernet *nft_net = nft_pernet(net);
+@@ -2482,6 +2483,7 @@ static struct nft_chain *nft_chain_looku
+               struct nft_chain *chain = trans->ctx.chain;
+               if (trans->msg_type == NFT_MSG_NEWCHAIN &&
++                  chain->table == table &&
+                   id == nft_trans_chain_id(trans))
+                       return chain;
+       }
+@@ -3417,7 +3419,7 @@ static int nf_tables_newrule(struct sk_b
+                       return -EOPNOTSUPP;
+       } else if (nla[NFTA_RULE_CHAIN_ID]) {
+-              chain = nft_chain_lookup_byid(net, nla[NFTA_RULE_CHAIN_ID]);
++              chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID]);
+               if (IS_ERR(chain)) {
+                       NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN_ID]);
+                       return PTR_ERR(chain);
+@@ -9607,7 +9609,7 @@ static int nft_verdict_init(const struct
+                                                tb[NFTA_VERDICT_CHAIN],
+                                                genmask);
+               } else if (tb[NFTA_VERDICT_CHAIN_ID]) {
+-                      chain = nft_chain_lookup_byid(ctx->net,
++                      chain = nft_chain_lookup_byid(ctx->net, ctx->table,
+                                                     tb[NFTA_VERDICT_CHAIN_ID]);
+                       if (IS_ERR(chain))
+                               return PTR_ERR(chain);
diff --git a/queue-5.19/netfilter-nf_tables-do-not-allow-rule_id-to-refer-to-another-chain.patch b/queue-5.19/netfilter-nf_tables-do-not-allow-rule_id-to-refer-to-another-chain.patch
new file mode 100644 (file)
index 0000000..c387345
--- /dev/null
@@ -0,0 +1,73 @@
+From 36d5b2913219ac853908b0f1c664345e04313856 Mon Sep 17 00:00:00 2001
+From: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
+Date: Tue, 9 Aug 2022 14:01:48 -0300
+Subject: netfilter: nf_tables: do not allow RULE_ID to refer to another chain
+
+From: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
+
+commit 36d5b2913219ac853908b0f1c664345e04313856 upstream.
+
+When doing lookups for rules on the same batch by using its ID, a rule from
+a different chain can be used. If a rule is added to a chain but tries to
+be positioned next to a rule from a different chain, it will be linked to
+chain2, but the use counter on chain1 would be the one to be incremented.
+
+When looking for rules by ID, use the chain that was used for the lookup by
+name. The chain used in the context copied to the transaction needs to
+match that same chain. That way, struct nft_rule does not need to get
+enlarged with another member.
+
+Fixes: 1a94e38d254b ("netfilter: nf_tables: add NFTA_RULE_ID attribute")
+Fixes: 75dd48e2e420 ("netfilter: nf_tables: Support RULE_ID reference in new rule")
+Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nf_tables_api.c |    7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -3373,6 +3373,7 @@ static int nft_table_validate(struct net
+ }
+ static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
++                                           const struct nft_chain *chain,
+                                            const struct nlattr *nla);
+ #define NFT_RULE_MAXEXPRS     128
+@@ -3461,7 +3462,7 @@ static int nf_tables_newrule(struct sk_b
+                               return PTR_ERR(old_rule);
+                       }
+               } else if (nla[NFTA_RULE_POSITION_ID]) {
+-                      old_rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_POSITION_ID]);
++                      old_rule = nft_rule_lookup_byid(net, chain, nla[NFTA_RULE_POSITION_ID]);
+                       if (IS_ERR(old_rule)) {
+                               NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION_ID]);
+                               return PTR_ERR(old_rule);
+@@ -3606,6 +3607,7 @@ err_release_expr:
+ }
+ static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
++                                           const struct nft_chain *chain,
+                                            const struct nlattr *nla)
+ {
+       struct nftables_pernet *nft_net = nft_pernet(net);
+@@ -3616,6 +3618,7 @@ static struct nft_rule *nft_rule_lookup_
+               struct nft_rule *rule = nft_trans_rule(trans);
+               if (trans->msg_type == NFT_MSG_NEWRULE &&
++                  trans->ctx.chain == chain &&
+                   id == nft_trans_rule_id(trans))
+                       return rule;
+       }
+@@ -3665,7 +3668,7 @@ static int nf_tables_delrule(struct sk_b
+                       err = nft_delrule(&ctx, rule);
+               } else if (nla[NFTA_RULE_ID]) {
+-                      rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_ID]);
++                      rule = nft_rule_lookup_byid(net, chain, nla[NFTA_RULE_ID]);
+                       if (IS_ERR(rule)) {
+                               NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_ID]);
+                               return PTR_ERR(rule);
diff --git a/queue-5.19/netfilter-nf_tables-do-not-allow-set_id-to-refer-to-another-table.patch b/queue-5.19/netfilter-nf_tables-do-not-allow-set_id-to-refer-to-another-table.patch
new file mode 100644 (file)
index 0000000..797e01c
--- /dev/null
@@ -0,0 +1,57 @@
+From 470ee20e069a6d05ae549f7d0ef2bdbcee6a81b2 Mon Sep 17 00:00:00 2001
+From: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
+Date: Tue, 9 Aug 2022 14:01:46 -0300
+Subject: netfilter: nf_tables: do not allow SET_ID to refer to another table
+
+From: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
+
+commit 470ee20e069a6d05ae549f7d0ef2bdbcee6a81b2 upstream.
+
+When doing lookups for sets on the same batch by using its ID, a set from a
+different table can be used.
+
+Then, when the table is removed, a reference to the set may be kept after
+the set is freed, leading to a potential use-after-free.
+
+When looking for sets by ID, use the table that was used for the lookup by
+name, and only return sets belonging to that same table.
+
+This fixes CVE-2022-2586, also reported as ZDI-CAN-17470.
+
+Reported-by: Team Orca of Sea Security (@seasecresponse)
+Fixes: 958bee14d071 ("netfilter: nf_tables: use new transaction infrastructure to handle sets")
+Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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
+@@ -3842,6 +3842,7 @@ static struct nft_set *nft_set_lookup_by
+ }
+ static struct nft_set *nft_set_lookup_byid(const struct net *net,
++                                         const struct nft_table *table,
+                                          const struct nlattr *nla, u8 genmask)
+ {
+       struct nftables_pernet *nft_net = nft_pernet(net);
+@@ -3853,6 +3854,7 @@ static struct nft_set *nft_set_lookup_by
+                       struct nft_set *set = nft_trans_set(trans);
+                       if (id == nft_trans_set_id(trans) &&
++                          set->table == table &&
+                           nft_active_genmask(set, genmask))
+                               return set;
+               }
+@@ -3873,7 +3875,7 @@ struct nft_set *nft_set_lookup_global(co
+               if (!nla_set_id)
+                       return set;
+-              set = nft_set_lookup_byid(net, nla_set_id, genmask);
++              set = nft_set_lookup_byid(net, table, nla_set_id, genmask);
+       }
+       return set;
+ }
diff --git a/queue-5.19/netfilter-nf_tables-fix-null-deref-due-to-zeroed-list-head.patch b/queue-5.19/netfilter-nf_tables-fix-null-deref-due-to-zeroed-list-head.patch
new file mode 100644 (file)
index 0000000..305f6ee
--- /dev/null
@@ -0,0 +1,45 @@
+From 580077855a40741cf511766129702d97ff02f4d9 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Tue, 9 Aug 2022 18:34:02 +0200
+Subject: netfilter: nf_tables: fix null deref due to zeroed list head
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 580077855a40741cf511766129702d97ff02f4d9 upstream.
+
+In nf_tables_updtable, if nf_tables_table_enable returns an error,
+nft_trans_destroy is called to free the transaction object.
+
+nft_trans_destroy() calls list_del(), but the transaction was never
+placed on a list -- the list head is all zeroes, this results in
+a null dereference:
+
+BUG: KASAN: null-ptr-deref in nft_trans_destroy+0x26/0x59
+Call Trace:
+ nft_trans_destroy+0x26/0x59
+ nf_tables_newtable+0x4bc/0x9bc
+ [..]
+
+Its sane to assume that nft_trans_destroy() can be called
+on the transaction object returned by nft_trans_alloc(), so
+make sure the list head is initialised.
+
+Fixes: 55dd6f93076b ("netfilter: nf_tables: use new transaction infrastructure to handle table")
+Reported-by: mingi cho <mgcho.minic@gmail.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nf_tables_api.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -153,6 +153,7 @@ static struct nft_trans *nft_trans_alloc
+       if (trans == NULL)
+               return NULL;
++      INIT_LIST_HEAD(&trans->list);
+       trans->msg_type = msg_type;
+       trans->ctx      = *ctx;
diff --git a/queue-5.19/netfilter-nf_tables-upfront-validation-of-data-via-nft_data_init.patch b/queue-5.19/netfilter-nf_tables-upfront-validation-of-data-via-nft_data_init.patch
new file mode 100644 (file)
index 0000000..8079d48
--- /dev/null
@@ -0,0 +1,513 @@
+From 341b6941608762d8235f3fd1e45e4d7114ed8c2c Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Mon, 8 Aug 2022 19:30:06 +0200
+Subject: netfilter: nf_tables: upfront validation of data via nft_data_init()
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+commit 341b6941608762d8235f3fd1e45e4d7114ed8c2c upstream.
+
+Instead of parsing the data and then validate that type and length are
+correct, pass a description of the expected data so it can be validated
+upfront before parsing it to bail out earlier.
+
+This patch adds a new .size field to specify the maximum size of the
+data area. The .len field is optional and it is used as an input/output
+field, it provides the specific length of the expected data in the input
+path. If then .len field is not specified, then obtained length from the
+netlink attribute is stored. This is required by cmp, bitwise, range and
+immediate, which provide no netlink attribute that describes the data
+length. The immediate expression uses the destination register type to
+infer the expected data type.
+
+Relying on opencoded validation of the expected data might lead to
+subtle bugs as described in 7e6bc1f6cabc ("netfilter: nf_tables:
+stricter validation of element data").
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/netfilter/nf_tables.h |    4 -
+ net/netfilter/nf_tables_api.c     |   78 +++++++++++++++++++-------------------
+ net/netfilter/nft_bitwise.c       |   66 ++++++++++++++++----------------
+ net/netfilter/nft_cmp.c           |   44 +++++++++------------
+ net/netfilter/nft_immediate.c     |   22 +++++++++-
+ net/netfilter/nft_range.c         |   27 +++++--------
+ 6 files changed, 126 insertions(+), 115 deletions(-)
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -208,11 +208,11 @@ struct nft_ctx {
+ struct nft_data_desc {
+       enum nft_data_types             type;
++      unsigned int                    size;
+       unsigned int                    len;
+ };
+-int nft_data_init(const struct nft_ctx *ctx,
+-                struct nft_data *data, unsigned int size,
++int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
+                 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);
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -5202,19 +5202,13 @@ static int nft_setelem_parse_flags(const
+ static int nft_setelem_parse_key(struct nft_ctx *ctx, struct nft_set *set,
+                                struct nft_data *key, struct nlattr *attr)
+ {
+-      struct nft_data_desc desc;
+-      int err;
+-
+-      err = nft_data_init(ctx, key, NFT_DATA_VALUE_MAXLEN, &desc, attr);
+-      if (err < 0)
+-              return err;
+-
+-      if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) {
+-              nft_data_release(key, desc.type);
+-              return -EINVAL;
+-      }
++      struct nft_data_desc desc = {
++              .type   = NFT_DATA_VALUE,
++              .size   = NFT_DATA_VALUE_MAXLEN,
++              .len    = set->klen,
++      };
+-      return 0;
++      return nft_data_init(ctx, key, &desc, attr);
+ }
+ static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set,
+@@ -5223,24 +5217,17 @@ static int nft_setelem_parse_data(struct
+                                 struct nlattr *attr)
+ {
+       u32 dtype;
+-      int err;
+-
+-      err = nft_data_init(ctx, data, NFT_DATA_VALUE_MAXLEN, desc, attr);
+-      if (err < 0)
+-              return err;
+       if (set->dtype == NFT_DATA_VERDICT)
+               dtype = NFT_DATA_VERDICT;
+       else
+               dtype = NFT_DATA_VALUE;
+-      if (dtype != desc->type ||
+-          set->dlen != desc->len) {
+-              nft_data_release(data, desc->type);
+-              return -EINVAL;
+-      }
++      desc->type = dtype;
++      desc->size = NFT_DATA_VALUE_MAXLEN;
++      desc->len = set->dlen;
+-      return 0;
++      return nft_data_init(ctx, data, desc, attr);
+ }
+ static void *nft_setelem_catchall_get(const struct net *net,
+@@ -9631,7 +9618,7 @@ static int nft_verdict_init(const struct
+       }
+       desc->len = sizeof(data->verdict);
+-      desc->type = NFT_DATA_VERDICT;
++
+       return 0;
+ }
+@@ -9684,20 +9671,25 @@ nla_put_failure:
+ }
+ static int nft_value_init(const struct nft_ctx *ctx,
+-                        struct nft_data *data, unsigned int size,
+-                        struct nft_data_desc *desc, const struct nlattr *nla)
++                        struct nft_data *data, struct nft_data_desc *desc,
++                        const struct nlattr *nla)
+ {
+       unsigned int len;
+       len = nla_len(nla);
+       if (len == 0)
+               return -EINVAL;
+-      if (len > size)
++      if (len > desc->size)
+               return -EOVERFLOW;
++      if (desc->len) {
++              if (len != desc->len)
++                      return -EINVAL;
++      } else {
++              desc->len = len;
++      }
+       nla_memcpy(data->data, nla, len);
+-      desc->type = NFT_DATA_VALUE;
+-      desc->len  = len;
++
+       return 0;
+ }
+@@ -9717,7 +9709,6 @@ static const struct nla_policy nft_data_
+  *
+  *    @ctx: context of the expression using the data
+  *    @data: destination struct nft_data
+- *    @size: maximum data length
+  *    @desc: data description
+  *    @nla: netlink attribute containing data
+  *
+@@ -9727,24 +9718,35 @@ static const struct nla_policy nft_data_
+  *    The caller can indicate that it only wants to accept data of type
+  *    NFT_DATA_VALUE by passing NULL for the ctx argument.
+  */
+-int nft_data_init(const struct nft_ctx *ctx,
+-                struct nft_data *data, unsigned int size,
++int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
+                 struct nft_data_desc *desc, const struct nlattr *nla)
+ {
+       struct nlattr *tb[NFTA_DATA_MAX + 1];
+       int err;
++      if (WARN_ON_ONCE(!desc->size))
++              return -EINVAL;
++
+       err = nla_parse_nested_deprecated(tb, NFTA_DATA_MAX, nla,
+                                         nft_data_policy, NULL);
+       if (err < 0)
+               return err;
+-      if (tb[NFTA_DATA_VALUE])
+-              return nft_value_init(ctx, data, size, desc,
+-                                    tb[NFTA_DATA_VALUE]);
+-      if (tb[NFTA_DATA_VERDICT] && ctx != NULL)
+-              return nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]);
+-      return -EINVAL;
++      if (tb[NFTA_DATA_VALUE]) {
++              if (desc->type != NFT_DATA_VALUE)
++                      return -EINVAL;
++
++              err = nft_value_init(ctx, data, desc, tb[NFTA_DATA_VALUE]);
++      } else if (tb[NFTA_DATA_VERDICT] && ctx != NULL) {
++              if (desc->type != NFT_DATA_VERDICT)
++                      return -EINVAL;
++
++              err = nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]);
++      } else {
++              err = -EINVAL;
++      }
++
++      return err;
+ }
+ EXPORT_SYMBOL_GPL(nft_data_init);
+--- a/net/netfilter/nft_bitwise.c
++++ b/net/netfilter/nft_bitwise.c
+@@ -93,7 +93,16 @@ static const struct nla_policy nft_bitwi
+ static int nft_bitwise_init_bool(struct nft_bitwise *priv,
+                                const struct nlattr *const tb[])
+ {
+-      struct nft_data_desc mask, xor;
++      struct nft_data_desc mask = {
++              .type   = NFT_DATA_VALUE,
++              .size   = sizeof(priv->mask),
++              .len    = priv->len,
++      };
++      struct nft_data_desc xor = {
++              .type   = NFT_DATA_VALUE,
++              .size   = sizeof(priv->xor),
++              .len    = priv->len,
++      };
+       int err;
+       if (tb[NFTA_BITWISE_DATA])
+@@ -103,37 +112,30 @@ static int nft_bitwise_init_bool(struct
+           !tb[NFTA_BITWISE_XOR])
+               return -EINVAL;
+-      err = nft_data_init(NULL, &priv->mask, sizeof(priv->mask), &mask,
+-                          tb[NFTA_BITWISE_MASK]);
++      err = nft_data_init(NULL, &priv->mask, &mask, tb[NFTA_BITWISE_MASK]);
+       if (err < 0)
+               return err;
+-      if (mask.type != NFT_DATA_VALUE || mask.len != priv->len) {
+-              err = -EINVAL;
+-              goto err_mask_release;
+-      }
+-      err = nft_data_init(NULL, &priv->xor, sizeof(priv->xor), &xor,
+-                          tb[NFTA_BITWISE_XOR]);
++      err = nft_data_init(NULL, &priv->xor, &xor, tb[NFTA_BITWISE_XOR]);
+       if (err < 0)
+-              goto err_mask_release;
+-      if (xor.type != NFT_DATA_VALUE || xor.len != priv->len) {
+-              err = -EINVAL;
+-              goto err_xor_release;
+-      }
++              goto err_xor_err;
+       return 0;
+-err_xor_release:
+-      nft_data_release(&priv->xor, xor.type);
+-err_mask_release:
++err_xor_err:
+       nft_data_release(&priv->mask, mask.type);
++
+       return err;
+ }
+ static int nft_bitwise_init_shift(struct nft_bitwise *priv,
+                                 const struct nlattr *const tb[])
+ {
+-      struct nft_data_desc d;
++      struct nft_data_desc desc = {
++              .type   = NFT_DATA_VALUE,
++              .size   = sizeof(priv->data),
++              .len    = sizeof(u32),
++      };
+       int err;
+       if (tb[NFTA_BITWISE_MASK] ||
+@@ -143,13 +145,12 @@ static int nft_bitwise_init_shift(struct
+       if (!tb[NFTA_BITWISE_DATA])
+               return -EINVAL;
+-      err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &d,
+-                          tb[NFTA_BITWISE_DATA]);
++      err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_BITWISE_DATA]);
+       if (err < 0)
+               return err;
+-      if (d.type != NFT_DATA_VALUE || d.len != sizeof(u32) ||
+-          priv->data.data[0] >= BITS_PER_TYPE(u32)) {
+-              nft_data_release(&priv->data, d.type);
++
++      if (priv->data.data[0] >= BITS_PER_TYPE(u32)) {
++              nft_data_release(&priv->data, desc.type);
+               return -EINVAL;
+       }
+@@ -339,22 +340,21 @@ static const struct nft_expr_ops nft_bit
+ static int
+ nft_bitwise_extract_u32_data(const struct nlattr * const tb, u32 *out)
+ {
+-      struct nft_data_desc desc;
+       struct nft_data data;
+-      int err = 0;
++      struct nft_data_desc desc = {
++              .type   = NFT_DATA_VALUE,
++              .size   = sizeof(data),
++              .len    = sizeof(u32),
++      };
++      int err;
+-      err = nft_data_init(NULL, &data, sizeof(data), &desc, tb);
++      err = nft_data_init(NULL, &data, &desc, tb);
+       if (err < 0)
+               return err;
+-      if (desc.type != NFT_DATA_VALUE || desc.len != sizeof(u32)) {
+-              err = -EINVAL;
+-              goto err;
+-      }
+       *out = data.data[0];
+-err:
+-      nft_data_release(&data, desc.type);
+-      return err;
++
++      return 0;
+ }
+ static int nft_bitwise_fast_init(const struct nft_ctx *ctx,
+--- a/net/netfilter/nft_cmp.c
++++ b/net/netfilter/nft_cmp.c
+@@ -73,20 +73,16 @@ static int nft_cmp_init(const struct nft
+                       const struct nlattr * const tb[])
+ {
+       struct nft_cmp_expr *priv = nft_expr_priv(expr);
+-      struct nft_data_desc desc;
++      struct nft_data_desc desc = {
++              .type   = NFT_DATA_VALUE,
++              .size   = sizeof(priv->data),
++      };
+       int err;
+-      err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc,
+-                          tb[NFTA_CMP_DATA]);
++      err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]);
+       if (err < 0)
+               return err;
+-      if (desc.type != NFT_DATA_VALUE) {
+-              err = -EINVAL;
+-              nft_data_release(&priv->data, desc.type);
+-              return err;
+-      }
+-
+       err = nft_parse_register_load(tb[NFTA_CMP_SREG], &priv->sreg, desc.len);
+       if (err < 0)
+               return err;
+@@ -202,12 +198,14 @@ static int nft_cmp_fast_init(const struc
+                            const struct nlattr * const tb[])
+ {
+       struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
+-      struct nft_data_desc desc;
+       struct nft_data data;
++      struct nft_data_desc desc = {
++              .type   = NFT_DATA_VALUE,
++              .size   = sizeof(data),
++      };
+       int err;
+-      err = nft_data_init(NULL, &data, sizeof(data), &desc,
+-                          tb[NFTA_CMP_DATA]);
++      err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
+       if (err < 0)
+               return err;
+@@ -301,11 +299,13 @@ static int nft_cmp16_fast_init(const str
+                              const struct nlattr * const tb[])
+ {
+       struct nft_cmp16_fast_expr *priv = nft_expr_priv(expr);
+-      struct nft_data_desc desc;
++      struct nft_data_desc desc = {
++              .type   = NFT_DATA_VALUE,
++              .size   = sizeof(priv->data),
++      };
+       int err;
+-      err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc,
+-                          tb[NFTA_CMP_DATA]);
++      err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]);
+       if (err < 0)
+               return err;
+@@ -368,8 +368,11 @@ const struct nft_expr_ops nft_cmp16_fast
+ static const struct nft_expr_ops *
+ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
+ {
+-      struct nft_data_desc desc;
+       struct nft_data data;
++      struct nft_data_desc desc = {
++              .type   = NFT_DATA_VALUE,
++              .size   = sizeof(data),
++      };
+       enum nft_cmp_ops op;
+       u8 sreg;
+       int err;
+@@ -392,14 +395,10 @@ nft_cmp_select_ops(const struct nft_ctx
+               return ERR_PTR(-EINVAL);
+       }
+-      err = nft_data_init(NULL, &data, sizeof(data), &desc,
+-                          tb[NFTA_CMP_DATA]);
++      err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
+       if (err < 0)
+               return ERR_PTR(err);
+-      if (desc.type != NFT_DATA_VALUE)
+-              goto err1;
+-
+       sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
+       if (op == NFT_CMP_EQ || op == NFT_CMP_NEQ) {
+@@ -411,9 +410,6 @@ nft_cmp_select_ops(const struct nft_ctx
+                       return &nft_cmp16_fast_ops;
+       }
+       return &nft_cmp_ops;
+-err1:
+-      nft_data_release(&data, desc.type);
+-      return ERR_PTR(-EINVAL);
+ }
+ struct nft_expr_type nft_cmp_type __read_mostly = {
+--- a/net/netfilter/nft_immediate.c
++++ b/net/netfilter/nft_immediate.c
+@@ -29,20 +29,36 @@ static const struct nla_policy nft_immed
+       [NFTA_IMMEDIATE_DATA]   = { .type = NLA_NESTED },
+ };
++static enum nft_data_types nft_reg_to_type(const struct nlattr *nla)
++{
++      enum nft_data_types type;
++      u8 reg;
++
++      reg = ntohl(nla_get_be32(nla));
++      if (reg == NFT_REG_VERDICT)
++              type = NFT_DATA_VERDICT;
++      else
++              type = NFT_DATA_VALUE;
++
++      return type;
++}
++
+ static int nft_immediate_init(const struct nft_ctx *ctx,
+                             const struct nft_expr *expr,
+                             const struct nlattr * const tb[])
+ {
+       struct nft_immediate_expr *priv = nft_expr_priv(expr);
+-      struct nft_data_desc desc;
++      struct nft_data_desc desc = {
++              .size   = sizeof(priv->data),
++      };
+       int err;
+       if (tb[NFTA_IMMEDIATE_DREG] == NULL ||
+           tb[NFTA_IMMEDIATE_DATA] == NULL)
+               return -EINVAL;
+-      err = nft_data_init(ctx, &priv->data, sizeof(priv->data), &desc,
+-                          tb[NFTA_IMMEDIATE_DATA]);
++      desc.type = nft_reg_to_type(tb[NFTA_IMMEDIATE_DREG]);
++      err = nft_data_init(ctx, &priv->data, &desc, tb[NFTA_IMMEDIATE_DATA]);
+       if (err < 0)
+               return err;
+--- a/net/netfilter/nft_range.c
++++ b/net/netfilter/nft_range.c
+@@ -51,7 +51,14 @@ static int nft_range_init(const struct n
+                       const struct nlattr * const tb[])
+ {
+       struct nft_range_expr *priv = nft_expr_priv(expr);
+-      struct nft_data_desc desc_from, desc_to;
++      struct nft_data_desc desc_from = {
++              .type   = NFT_DATA_VALUE,
++              .size   = sizeof(priv->data_from),
++      };
++      struct nft_data_desc desc_to = {
++              .type   = NFT_DATA_VALUE,
++              .size   = sizeof(priv->data_to),
++      };
+       int err;
+       u32 op;
+@@ -61,26 +68,16 @@ static int nft_range_init(const struct n
+           !tb[NFTA_RANGE_TO_DATA])
+               return -EINVAL;
+-      err = nft_data_init(NULL, &priv->data_from, sizeof(priv->data_from),
+-                          &desc_from, tb[NFTA_RANGE_FROM_DATA]);
++      err = nft_data_init(NULL, &priv->data_from, &desc_from,
++                          tb[NFTA_RANGE_FROM_DATA]);
+       if (err < 0)
+               return err;
+-      if (desc_from.type != NFT_DATA_VALUE) {
+-              err = -EINVAL;
+-              goto err1;
+-      }
+-
+-      err = nft_data_init(NULL, &priv->data_to, sizeof(priv->data_to),
+-                          &desc_to, tb[NFTA_RANGE_TO_DATA]);
++      err = nft_data_init(NULL, &priv->data_to, &desc_to,
++                          tb[NFTA_RANGE_TO_DATA]);
+       if (err < 0)
+               goto err1;
+-      if (desc_to.type != NFT_DATA_VALUE) {
+-              err = -EINVAL;
+-              goto err2;
+-      }
+-
+       if (desc_from.len != desc_to.len) {
+               err = -EINVAL;
+               goto err2;
index ff5d6324942ae4fdc86021f41668b36dab8cdb19..5fde6c15da628958b878b334e7b9bf96b6e281fd 100644 (file)
@@ -137,3 +137,10 @@ arm64-dts-uniphier-fix-usb-interrupts-for-pxs3-soc.patch
 usb-gadget-fix-use-after-free-read-in-usb_udc_uevent.patch
 usb-dwc3-gadget-refactor-dwc3_repare_one_trb.patch
 usb-dwc3-gadget-fix-high-speed-multiplier-setting.patch
+netfilter-nf_tables-do-not-allow-set_id-to-refer-to-another-table.patch
+netfilter-nf_tables-do-not-allow-chain_id-to-refer-to-another-table.patch
+netfilter-nf_tables-do-not-allow-rule_id-to-refer-to-another-chain.patch
+netfilter-nf_tables-upfront-validation-of-data-via-nft_data_init.patch
+netfilter-nf_tables-disallow-jump-to-implicit-chain-from-set-element.patch
+netfilter-nf_tables-fix-null-deref-due-to-zeroed-list-head.patch
+epoll-autoremove-wakers-even-more-aggressively.patch