--- /dev/null
+From 0ecd74260f37da7bcb66823aca68457f00ac061d Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Tue, 5 Feb 2019 17:57:27 +0100
+Subject: ALSA: hda/ca0132 - Fix build error without CONFIG_PCI
+
+[ Upstream commit c97617a81a7616d49bc3700959e08c6c6f447093 ]
+
+A call of pci_iounmap() call without CONFIG_PCI leads to a build error
+on some architectures. We tried to address this and add a check of
+IS_ENABLED(CONFIG_PCI), but this still doesn't seem enough for sh.
+Ideally we should fix it globally, it's really a corner case, so let's
+paper over it with a simpler ifdef.
+
+Fixes: 1e73359a24fa ("ALSA: hda/ca0132 - make pci_iounmap() call conditional")
+Reported-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/pci/hda/patch_ca0132.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
+index 80f73810b21b..0436789e7cd8 100644
+--- a/sound/pci/hda/patch_ca0132.c
++++ b/sound/pci/hda/patch_ca0132.c
+@@ -7394,8 +7394,10 @@ static void ca0132_free(struct hda_codec *codec)
+ ca0132_exit_chip(codec);
+
+ snd_hda_power_down(codec);
+- if (IS_ENABLED(CONFIG_PCI) && spec->mem_base)
++#ifdef CONFIG_PCI
++ if (spec->mem_base)
+ pci_iounmap(codec->bus->pci, spec->mem_base);
++#endif
+ kfree(spec->spec_init_verbs);
+ kfree(codec->spec);
+ }
+--
+2.19.1
+
--- /dev/null
+From 906e35d2764c3df0836120e2aed8b9e4abcbcc3b Mon Sep 17 00:00:00 2001
+From: Damian Kos <dkos@cadence.com>
+Date: Mon, 19 Nov 2018 15:14:14 +0000
+Subject: drm/rockchip: fix for mailbox read validation.
+
+[ Upstream commit e4056bbb6719fe713bfc4030ac78e8e97ddf7574 ]
+
+This is basically the same fix as in
+commit fa68d4f8476b ("drm/rockchip: fix for mailbox read size")
+but for cdn_dp_mailbox_validate_receive function.
+
+See patchwork.kernel.org/patch/10671981/ for details.
+
+Signed-off-by: Damian Kos <dkos@cadence.com>
+Signed-off-by: Heiko Stuebner <heiko@sntech.de>
+Link: https://patchwork.freedesktop.org/patch/msgid/1542640463-18332-1-git-send-email-dkos@cadence.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/rockchip/cdn-dp-reg.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+index 5a485489a1e2..6c8b14fb1d2f 100644
+--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
++++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+@@ -113,7 +113,7 @@ static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val)
+
+ static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
+ u8 module_id, u8 opcode,
+- u8 req_size)
++ u16 req_size)
+ {
+ u32 mbox_size, i;
+ u8 header[4];
+--
+2.19.1
+
--- /dev/null
+From d7c94b111be50a85c94c89462a7d091e64ca448d Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Thu, 21 Feb 2019 11:17:34 -0500
+Subject: ext4: fix some error pointer dereferences
+
+[ Upstream commit 7159a986b4202343f6cca3bb8079ecace5816fd6 ]
+
+We can't pass error pointers to brelse().
+
+Fixes: fb265c9cb49e ("ext4: add ext4_sb_bread() to disambiguate ENOMEM cases")
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Reviewed-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ext4/xattr.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
+index c0ba5206cd9d..006c277dc22e 100644
+--- a/fs/ext4/xattr.c
++++ b/fs/ext4/xattr.c
+@@ -829,6 +829,7 @@ int ext4_get_inode_usage(struct inode *inode, qsize_t *usage)
+ bh = ext4_sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl, REQ_PRIO);
+ if (IS_ERR(bh)) {
+ ret = PTR_ERR(bh);
++ bh = NULL;
+ goto out;
+ }
+
+@@ -2907,6 +2908,7 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
+ if (error == -EIO)
+ EXT4_ERROR_INODE(inode, "block %llu read error",
+ EXT4_I(inode)->i_file_acl);
++ bh = NULL;
+ goto cleanup;
+ }
+ error = ext4_xattr_check_block(inode, bh);
+@@ -3063,6 +3065,7 @@ ext4_xattr_block_cache_find(struct inode *inode,
+ if (IS_ERR(bh)) {
+ if (PTR_ERR(bh) == -ENOMEM)
+ return NULL;
++ bh = NULL;
+ EXT4_ERROR_INODE(inode, "block %lu read error",
+ (unsigned long)ce->e_value);
+ } else if (ext4_xattr_cmp(header, BHDR(bh)) == 0) {
+--
+2.19.1
+
--- /dev/null
+From 3b94e7e7789719a3bcf30dc6ac09d42ca187975d Mon Sep 17 00:00:00 2001
+From: Andrea Claudi <aclaudi@redhat.com>
+Date: Fri, 15 Feb 2019 17:51:48 +0100
+Subject: ipvs: fix warning on unused variable
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+[ Upstream commit c93a49b9769e435990c82297aa0baa31e1538790 ]
+
+When CONFIG_IP_VS_IPV6 is not defined, build produced this warning:
+
+net/netfilter/ipvs/ip_vs_ctl.c:899:6: warning: unused variable ‘ret’ [-Wunused-variable]
+ int ret = 0;
+ ^~~
+
+Fix this by moving the declaration of 'ret' in the CONFIG_IP_VS_IPV6
+section in the same function.
+
+While at it, drop its unneeded initialisation.
+
+Fixes: 098e13f5b21d ("ipvs: fix dependency on nf_defrag_ipv6")
+Reported-by: Stefano Brivio <sbrivio@redhat.com>
+Signed-off-by: Andrea Claudi <aclaudi@redhat.com>
+Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/ipvs/ip_vs_ctl.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
+index 8fd8d06454d6..2d4e048762f6 100644
+--- a/net/netfilter/ipvs/ip_vs_ctl.c
++++ b/net/netfilter/ipvs/ip_vs_ctl.c
+@@ -896,12 +896,13 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest,
+ {
+ struct ip_vs_dest *dest;
+ unsigned int atype, i;
+- int ret = 0;
+
+ EnterFunction(2);
+
+ #ifdef CONFIG_IP_VS_IPV6
+ if (udest->af == AF_INET6) {
++ int ret;
++
+ atype = ipv6_addr_type(&udest->addr.in6);
+ if ((!(atype & IPV6_ADDR_UNICAST) ||
+ atype & IPV6_ADDR_LINKLOCAL) &&
+--
+2.19.1
+
--- /dev/null
+From ff9f726364fdb4ef0e4335d9e822dc8ed9edff24 Mon Sep 17 00:00:00 2001
+From: Dongli Zhang <dongli.zhang@oracle.com>
+Date: Fri, 22 Feb 2019 22:10:19 +0800
+Subject: loop: do not print warn message if partition scan is successful
+
+[ Upstream commit 40853d6fc619a6fd3d3177c3973a2eac9b598a80 ]
+
+Do not print warn message when the partition scan returns 0.
+
+Fixes: d57f3374ba48 ("loop: Move special partition reread handling in loop_clr_fd()")
+Signed-off-by: Dongli Zhang <dongli.zhang@oracle.com>
+Reviewed-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/block/loop.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/block/loop.c b/drivers/block/loop.c
+index a63da9e07341..f1e63eb7cbca 100644
+--- a/drivers/block/loop.c
++++ b/drivers/block/loop.c
+@@ -1112,8 +1112,9 @@ out_unlock:
+ err = __blkdev_reread_part(bdev);
+ else
+ err = blkdev_reread_part(bdev);
+- pr_warn("%s: partition scan of loop%d failed (rc=%d)\n",
+- __func__, lo_number, err);
++ if (err)
++ pr_warn("%s: partition scan of loop%d failed (rc=%d)\n",
++ __func__, lo_number, err);
+ /* Device is gone, no point in returning error */
+ err = 0;
+ }
+--
+2.19.1
+
--- /dev/null
+From cba0ee0aae036508a290a2a24ed8a9eeac9ba4d2 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Mon, 4 Mar 2019 19:39:03 +0100
+Subject: net: dsa: mv88e6xxx: add call to mv88e6xxx_ports_cmode_init to probe
+ for new DSA framework
+
+[ Upstream commit 3acca1dd17060332cfab15693733cdaf9fba1c90 ]
+
+In the original patch I missed to add mv88e6xxx_ports_cmode_init()
+to the second probe function, the one for the new DSA framework.
+
+Fixes: ed8fe20205ac ("net: dsa: mv88e6xxx: prevent interrupt storm caused by mv88e6390x_port_set_cmode")
+Reported-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
+Suggested-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/dsa/mv88e6xxx/chip.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
+index dabe89968a78..2caa5c0c2bc4 100644
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -4821,6 +4821,7 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
+ if (err)
+ goto out;
+
++ mv88e6xxx_ports_cmode_init(chip);
+ mv88e6xxx_phy_init(chip);
+
+ if (chip->info->ops->get_eeprom) {
+--
+2.19.1
+
--- /dev/null
+From 6ab72aac366e00516bc711659720ae8bf2e51db7 Mon Sep 17 00:00:00 2001
+From: Thomas Falcon <tlfalcon@linux.ibm.com>
+Date: Fri, 30 Nov 2018 10:59:08 -0600
+Subject: net/ibmvnic: Fix RTNL deadlock during device reset
+
+[ Upstream commit 986103e7920cabc0b910749e77ae5589d3934d52 ]
+
+Commit a5681e20b541 ("net/ibmnvic: Fix deadlock problem
+in reset") made the change to hold the RTNL lock during
+driver reset but still calls netdev_notify_peers, which
+results in a deadlock. Instead, use call_netdevice_notifiers,
+which is functionally the same except that it does not
+take the RTNL lock again.
+
+Fixes: a5681e20b541 ("net/ibmnvic: Fix deadlock problem in reset")
+Signed-off-by: Thomas Falcon <tlfalcon@linux.ibm.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/ibm/ibmvnic.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
+index a475f36ddf8c..426789e2c23d 100644
+--- a/drivers/net/ethernet/ibm/ibmvnic.c
++++ b/drivers/net/ethernet/ibm/ibmvnic.c
+@@ -1859,7 +1859,7 @@ static int do_reset(struct ibmvnic_adapter *adapter,
+
+ if (adapter->reset_reason != VNIC_RESET_FAILOVER &&
+ adapter->reset_reason != VNIC_RESET_CHANGE_PARAM)
+- netdev_notify_peers(netdev);
++ call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, netdev);
+
+ netif_carrier_on(netdev);
+
+--
+2.19.1
+
--- /dev/null
+From f642c21f2f0efea07d99b8c641809bd9c1599608 Mon Sep 17 00:00:00 2001
+From: Antoine Tenart <antoine.tenart@bootlin.com>
+Date: Fri, 1 Mar 2019 11:52:08 +0100
+Subject: net: mvpp2: fix validate for PPv2.1
+
+[ Upstream commit 8b318f30ab4ef9bbc1241e6f8c1db366dbd347f2 ]
+
+The Phylink validate function is the Marvell PPv2 driver makes a check
+on the GoP id. This is valid an has to be done when using PPv2.2 engines
+but makes no sense when using PPv2.1. The check done when using an RGMII
+interface makes sure the GoP id is not 0, but this breaks PPv2.1. Fixes
+it.
+
+Fixes: 0fb628f0f250 ("net: mvpp2: fix phylink handling of invalid PHY modes")
+Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+index 9988c89ed9fd..9b10abb604cb 100644
+--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+@@ -4272,7 +4272,7 @@ static void mvpp2_phylink_validate(struct net_device *dev,
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+- if (port->gop_id == 0)
++ if (port->priv->hw_version == MVPP22 && port->gop_id == 0)
+ goto empty_set;
+ break;
+ default:
+--
+2.19.1
+
--- /dev/null
+From 3efac4b5c249a680c5fa3ca4e1ba8c0c2f70973c Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Thu, 14 Mar 2019 10:50:20 +0100
+Subject: netfilter: nf_tables: bogus EBUSY in helper removal from transaction
+
+[ Upstream commit 8ffcd32f64633926163cdd07a7d295c500a947d1 ]
+
+Proper use counter updates when activating and deactivating the object,
+otherwise, this hits bogus EBUSY error.
+
+Fixes: cd5125d8f518 ("netfilter: nf_tables: split set destruction in deactivate and destroy phase")
+Reported-by: Laura Garcia <nevola@gmail.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nft_objref.c | 19 ++++++++++++++++---
+ 1 file changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c
+index d8737c115257..bf92a40dd1b2 100644
+--- a/net/netfilter/nft_objref.c
++++ b/net/netfilter/nft_objref.c
+@@ -64,21 +64,34 @@ nla_put_failure:
+ return -1;
+ }
+
+-static void nft_objref_destroy(const struct nft_ctx *ctx,
+- const struct nft_expr *expr)
++static void nft_objref_deactivate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr,
++ enum nft_trans_phase phase)
+ {
+ struct nft_object *obj = nft_objref_priv(expr);
+
++ if (phase == NFT_TRANS_COMMIT)
++ return;
++
+ obj->use--;
+ }
+
++static void nft_objref_activate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ struct nft_object *obj = nft_objref_priv(expr);
++
++ obj->use++;
++}
++
+ static struct nft_expr_type nft_objref_type;
+ static const struct nft_expr_ops nft_objref_ops = {
+ .type = &nft_objref_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_object *)),
+ .eval = nft_objref_eval,
+ .init = nft_objref_init,
+- .destroy = nft_objref_destroy,
++ .activate = nft_objref_activate,
++ .deactivate = nft_objref_deactivate,
+ .dump = nft_objref_dump,
+ };
+
+--
+2.19.1
+
--- /dev/null
+From 34d7ff470b8db0226aaf121b5ef37dd2071b2d1a Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Fri, 8 Mar 2019 15:30:03 +0100
+Subject: netfilter: nf_tables: bogus EBUSY when deleting set after flush
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+[ Upstream commit 273fe3f1006ea5ebc63d6729e43e8e45e32b256a ]
+
+Set deletion after flush coming in the same batch results in EBUSY. Add
+set use counter to track the number of references to this set from
+rules. We cannot rely on the list of bindings for this since such list
+is still populated from the preparation phase.
+
+Reported-by: Václav Zindulka <vaclav.zindulka@tlapnet.cz>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/netfilter/nf_tables.h | 6 ++++++
+ net/netfilter/nf_tables_api.c | 28 +++++++++++++++++++++++++++-
+ net/netfilter/nft_dynset.c | 13 +++++++++----
+ net/netfilter/nft_lookup.c | 13 +++++++++----
+ net/netfilter/nft_objref.c | 13 +++++++++----
+ 5 files changed, 60 insertions(+), 13 deletions(-)
+
+diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
+index e5f879efcc92..f2be5d041ba3 100644
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -382,6 +382,7 @@ void nft_unregister_set(struct nft_set_type *type);
+ * @dtype: data type (verdict or numeric type defined by userspace)
+ * @objtype: object type (see NFT_OBJECT_* definitions)
+ * @size: maximum set size
++ * @use: number of rules references to this set
+ * @nelems: number of elements
+ * @ndeact: number of deactivated elements queued for removal
+ * @timeout: default timeout value in jiffies
+@@ -407,6 +408,7 @@ struct nft_set {
+ u32 dtype;
+ u32 objtype;
+ u32 size;
++ u32 use;
+ atomic_t nelems;
+ u32 ndeact;
+ u64 timeout;
+@@ -467,6 +469,10 @@ struct nft_set_binding {
+ u32 flags;
+ };
+
++enum nft_trans_phase;
++void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,
++ struct nft_set_binding *binding,
++ enum nft_trans_phase phase);
+ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
+ struct nft_set_binding *binding);
+ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
+diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
+index 959f123c1cf7..1af54119bafc 100644
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -3585,6 +3585,9 @@ err1:
+
+ static void nft_set_destroy(struct nft_set *set)
+ {
++ if (WARN_ON(set->use > 0))
++ return;
++
+ set->ops->destroy(set);
+ module_put(to_set_type(set->ops)->owner);
+ kfree(set->name);
+@@ -3625,7 +3628,7 @@ static int nf_tables_delset(struct net *net, struct sock *nlsk,
+ NL_SET_BAD_ATTR(extack, attr);
+ return PTR_ERR(set);
+ }
+- if (!list_empty(&set->bindings) ||
++ if (set->use ||
+ (nlh->nlmsg_flags & NLM_F_NONREC && atomic_read(&set->nelems) > 0)) {
+ NL_SET_BAD_ATTR(extack, attr);
+ return -EBUSY;
+@@ -3655,6 +3658,9 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
+ struct nft_set_binding *i;
+ struct nft_set_iter iter;
+
++ if (set->use == UINT_MAX)
++ return -EOVERFLOW;
++
+ if (!list_empty(&set->bindings) && nft_set_is_anonymous(set))
+ return -EBUSY;
+
+@@ -3682,6 +3688,7 @@ bind:
+ binding->chain = ctx->chain;
+ list_add_tail_rcu(&binding->list, &set->bindings);
+ nft_set_trans_bind(ctx, set);
++ set->use++;
+
+ return 0;
+ }
+@@ -3701,6 +3708,25 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
+ }
+ EXPORT_SYMBOL_GPL(nf_tables_unbind_set);
+
++void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,
++ struct nft_set_binding *binding,
++ enum nft_trans_phase phase)
++{
++ switch (phase) {
++ case NFT_TRANS_PREPARE:
++ set->use--;
++ return;
++ case NFT_TRANS_ABORT:
++ case NFT_TRANS_RELEASE:
++ set->use--;
++ /* fall through */
++ default:
++ nf_tables_unbind_set(ctx, set, binding,
++ phase == NFT_TRANS_COMMIT);
++ }
++}
++EXPORT_SYMBOL_GPL(nf_tables_deactivate_set);
++
+ void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set)
+ {
+ if (list_empty(&set->bindings) && nft_set_is_anonymous(set))
+diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
+index f1172f99752b..eb7f9a5f2aeb 100644
+--- a/net/netfilter/nft_dynset.c
++++ b/net/netfilter/nft_dynset.c
+@@ -241,11 +241,15 @@ static void nft_dynset_deactivate(const struct nft_ctx *ctx,
+ {
+ struct nft_dynset *priv = nft_expr_priv(expr);
+
+- if (phase == NFT_TRANS_PREPARE)
+- return;
++ nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase);
++}
++
++static void nft_dynset_activate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ struct nft_dynset *priv = nft_expr_priv(expr);
+
+- nf_tables_unbind_set(ctx, priv->set, &priv->binding,
+- phase == NFT_TRANS_COMMIT);
++ priv->set->use++;
+ }
+
+ static void nft_dynset_destroy(const struct nft_ctx *ctx,
+@@ -293,6 +297,7 @@ static const struct nft_expr_ops nft_dynset_ops = {
+ .eval = nft_dynset_eval,
+ .init = nft_dynset_init,
+ .destroy = nft_dynset_destroy,
++ .activate = nft_dynset_activate,
+ .deactivate = nft_dynset_deactivate,
+ .dump = nft_dynset_dump,
+ };
+diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c
+index 14496da5141d..161c3451a747 100644
+--- a/net/netfilter/nft_lookup.c
++++ b/net/netfilter/nft_lookup.c
+@@ -127,11 +127,15 @@ static void nft_lookup_deactivate(const struct nft_ctx *ctx,
+ {
+ struct nft_lookup *priv = nft_expr_priv(expr);
+
+- if (phase == NFT_TRANS_PREPARE)
+- return;
++ nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase);
++}
++
++static void nft_lookup_activate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ struct nft_lookup *priv = nft_expr_priv(expr);
+
+- nf_tables_unbind_set(ctx, priv->set, &priv->binding,
+- phase == NFT_TRANS_COMMIT);
++ priv->set->use++;
+ }
+
+ static void nft_lookup_destroy(const struct nft_ctx *ctx,
+@@ -222,6 +226,7 @@ static const struct nft_expr_ops nft_lookup_ops = {
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)),
+ .eval = nft_lookup_eval,
+ .init = nft_lookup_init,
++ .activate = nft_lookup_activate,
+ .deactivate = nft_lookup_deactivate,
+ .destroy = nft_lookup_destroy,
+ .dump = nft_lookup_dump,
+diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c
+index ae178e914486..d8737c115257 100644
+--- a/net/netfilter/nft_objref.c
++++ b/net/netfilter/nft_objref.c
+@@ -161,11 +161,15 @@ static void nft_objref_map_deactivate(const struct nft_ctx *ctx,
+ {
+ struct nft_objref_map *priv = nft_expr_priv(expr);
+
+- if (phase == NFT_TRANS_PREPARE)
+- return;
++ nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase);
++}
++
++static void nft_objref_map_activate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ struct nft_objref_map *priv = nft_expr_priv(expr);
+
+- nf_tables_unbind_set(ctx, priv->set, &priv->binding,
+- phase == NFT_TRANS_COMMIT);
++ priv->set->use++;
+ }
+
+ static void nft_objref_map_destroy(const struct nft_ctx *ctx,
+@@ -182,6 +186,7 @@ static const struct nft_expr_ops nft_objref_map_ops = {
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)),
+ .eval = nft_objref_map_eval,
+ .init = nft_objref_map_init,
++ .activate = nft_objref_map_activate,
+ .deactivate = nft_objref_map_deactivate,
+ .destroy = nft_objref_map_destroy,
+ .dump = nft_objref_map_dump,
+--
+2.19.1
+
--- /dev/null
+From 065a6f5981ae7b110a1825f65712096e2897e29b Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Fri, 8 Mar 2019 00:58:53 +0100
+Subject: netfilter: nf_tables: fix set double-free in abort path
+
+[ Upstream commit 40ba1d9b4d19796afc9b7ece872f5f3e8f5e2c13 ]
+
+The abort path can cause a double-free of an anonymous set.
+Added-and-to-be-aborted rule looks like this:
+
+udp dport { 137, 138 } drop
+
+The to-be-aborted transaction list looks like this:
+
+newset
+newsetelem
+newsetelem
+rule
+
+This gets walked in reverse order, so first pass disables the rule, the
+set elements, then the set.
+
+After synchronize_rcu(), we then destroy those in same order: rule, set
+element, set element, newset.
+
+Problem is that the anonymous set has already been bound to the rule, so
+the rule (lookup expression destructor) already frees the set, when then
+cause use-after-free when trying to delete the elements from this set,
+then try to free the set again when handling the newset expression.
+
+Rule releases the bound set in first place from the abort path, this
+causes the use-after-free on set element removal when undoing the new
+element transactions. To handle this, skip new element transaction if
+set is bound from the abort path.
+
+This is still causes the use-after-free on set element removal. To
+handle this, remove transaction from the list when the set is already
+bound.
+
+Joint work with Florian Westphal.
+
+Fixes: f6ac85858976 ("netfilter: nf_tables: unbind set in rule from commit path")
+Bugzilla: https://bugzilla.netfilter.org/show_bug.cgi?id=1325
+Acked-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/netfilter/nf_tables.h | 6 ++----
+ net/netfilter/nf_tables_api.c | 17 +++++++++++------
+ 2 files changed, 13 insertions(+), 10 deletions(-)
+
+diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
+index f66bb406004b..e5f879efcc92 100644
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -416,7 +416,8 @@ struct nft_set {
+ unsigned char *udata;
+ /* runtime data below here */
+ const struct nft_set_ops *ops ____cacheline_aligned;
+- u16 flags:14,
++ u16 flags:13,
++ bound:1,
+ genmask:2;
+ u8 klen;
+ u8 dlen;
+@@ -1330,15 +1331,12 @@ struct nft_trans_rule {
+ struct nft_trans_set {
+ struct nft_set *set;
+ u32 set_id;
+- bool bound;
+ };
+
+ #define nft_trans_set(trans) \
+ (((struct nft_trans_set *)trans->data)->set)
+ #define nft_trans_set_id(trans) \
+ (((struct nft_trans_set *)trans->data)->set_id)
+-#define nft_trans_set_bound(trans) \
+- (((struct nft_trans_set *)trans->data)->bound)
+
+ struct nft_trans_chain {
+ bool update;
+diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
+index de5908d51758..959f123c1cf7 100644
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -123,7 +123,7 @@ static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
+ list_for_each_entry_reverse(trans, &net->nft.commit_list, list) {
+ if (trans->msg_type == NFT_MSG_NEWSET &&
+ nft_trans_set(trans) == set) {
+- nft_trans_set_bound(trans) = true;
++ set->bound = true;
+ break;
+ }
+ }
+@@ -6547,8 +6547,7 @@ static void nf_tables_abort_release(struct nft_trans *trans)
+ nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
+ break;
+ case NFT_MSG_NEWSET:
+- if (!nft_trans_set_bound(trans))
+- nft_set_destroy(nft_trans_set(trans));
++ nft_set_destroy(nft_trans_set(trans));
+ break;
+ case NFT_MSG_NEWSETELEM:
+ nft_set_elem_destroy(nft_trans_elem_set(trans),
+@@ -6621,8 +6620,11 @@ static int __nf_tables_abort(struct net *net)
+ break;
+ case NFT_MSG_NEWSET:
+ trans->ctx.table->use--;
+- if (!nft_trans_set_bound(trans))
+- list_del_rcu(&nft_trans_set(trans)->list);
++ if (nft_trans_set(trans)->bound) {
++ nft_trans_destroy(trans);
++ break;
++ }
++ list_del_rcu(&nft_trans_set(trans)->list);
+ break;
+ case NFT_MSG_DELSET:
+ trans->ctx.table->use++;
+@@ -6630,8 +6632,11 @@ static int __nf_tables_abort(struct net *net)
+ nft_trans_destroy(trans);
+ break;
+ case NFT_MSG_NEWSETELEM:
++ if (nft_trans_elem_set(trans)->bound) {
++ nft_trans_destroy(trans);
++ break;
++ }
+ te = (struct nft_trans_elem *)trans->data;
+-
+ te->set->ops->remove(net, te->set, &te->elem);
+ atomic_dec(&te->set->nelems);
+ break;
+--
+2.19.1
+
--- /dev/null
+From f5faae44f51f956af30ef1495f50058f616516a8 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Wed, 29 Aug 2018 14:41:30 +0200
+Subject: netfilter: nf_tables: split set destruction in deactivate and destroy
+ phase
+
+[ Upstream commit cd5125d8f51882279f50506bb9c7e5e89dc9bef3 ]
+
+Splits unbind_set into destroy_set and unbinding operation.
+
+Unbinding removes set from lists (so new transaction would not
+find it anymore) but keeps memory allocated (so packet path continues
+to work).
+
+Rebind function is added to allow unrolling in case transaction
+that wants to remove set is aborted.
+
+Destroy function is added to free the memory, but this could occur
+outside of transaction in the future.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/netfilter/nf_tables.h | 7 +++++-
+ net/netfilter/nf_tables_api.c | 36 +++++++++++++++++++++----------
+ net/netfilter/nft_dynset.c | 21 +++++++++++++++++-
+ net/netfilter/nft_lookup.c | 20 ++++++++++++++++-
+ net/netfilter/nft_objref.c | 20 ++++++++++++++++-
+ 5 files changed, 89 insertions(+), 15 deletions(-)
+
+diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
+index 0f39ac487012..2c33958f3e7a 100644
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -470,6 +470,9 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
+ struct nft_set_binding *binding);
+ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
+ struct nft_set_binding *binding);
++void nf_tables_rebind_set(const struct nft_ctx *ctx, struct nft_set *set,
++ struct nft_set_binding *binding);
++void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set);
+
+ /**
+ * enum nft_set_extensions - set extension type IDs
+@@ -724,7 +727,9 @@ struct nft_expr_type {
+ * @eval: Expression evaluation function
+ * @size: full expression size, including private data size
+ * @init: initialization function
+- * @destroy: destruction function
++ * @activate: activate expression in the next generation
++ * @deactivate: deactivate expression in next generation
++ * @destroy: destruction function, called after synchronize_rcu
+ * @dump: function to dump parameters
+ * @type: expression type
+ * @validate: validate expression, called during loop detection
+diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
+index c06393fc716d..667f6eccbec7 100644
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -301,7 +301,7 @@ static int nft_delrule_by_chain(struct nft_ctx *ctx)
+ return 0;
+ }
+
+-static int nft_trans_set_add(struct nft_ctx *ctx, int msg_type,
++static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
+ struct nft_set *set)
+ {
+ struct nft_trans *trans;
+@@ -321,7 +321,7 @@ static int nft_trans_set_add(struct nft_ctx *ctx, int msg_type,
+ return 0;
+ }
+
+-static int nft_delset(struct nft_ctx *ctx, struct nft_set *set)
++static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set)
+ {
+ int err;
+
+@@ -3568,13 +3568,6 @@ static void nft_set_destroy(struct nft_set *set)
+ kvfree(set);
+ }
+
+-static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
+-{
+- list_del_rcu(&set->list);
+- nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
+- nft_set_destroy(set);
+-}
+-
+ static int nf_tables_delset(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const nla[],
+@@ -3669,17 +3662,38 @@ bind:
+ }
+ EXPORT_SYMBOL_GPL(nf_tables_bind_set);
+
+-void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
++void nf_tables_rebind_set(const struct nft_ctx *ctx, struct nft_set *set,
+ struct nft_set_binding *binding)
++{
++ if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
++ nft_is_active(ctx->net, set))
++ list_add_tail_rcu(&set->list, &ctx->table->sets);
++
++ list_add_tail_rcu(&binding->list, &set->bindings);
++}
++EXPORT_SYMBOL_GPL(nf_tables_rebind_set);
++
++void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
++ struct nft_set_binding *binding)
+ {
+ list_del_rcu(&binding->list);
+
+ if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
+ nft_is_active(ctx->net, set))
+- nf_tables_set_destroy(ctx, set);
++ list_del_rcu(&set->list);
+ }
+ EXPORT_SYMBOL_GPL(nf_tables_unbind_set);
+
++void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set)
++{
++ if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
++ nft_is_active(ctx->net, set)) {
++ nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
++ nft_set_destroy(set);
++ }
++}
++EXPORT_SYMBOL_GPL(nf_tables_destroy_set);
++
+ const struct nft_set_ext_type nft_set_ext_types[] = {
+ [NFT_SET_EXT_KEY] = {
+ .align = __alignof__(u32),
+diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
+index 6e91a37d57f2..07d4efd3d851 100644
+--- a/net/netfilter/nft_dynset.c
++++ b/net/netfilter/nft_dynset.c
+@@ -235,14 +235,31 @@ err1:
+ return err;
+ }
+
++static void nft_dynset_activate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ struct nft_dynset *priv = nft_expr_priv(expr);
++
++ nf_tables_rebind_set(ctx, priv->set, &priv->binding);
++}
++
++static void nft_dynset_deactivate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ struct nft_dynset *priv = nft_expr_priv(expr);
++
++ nf_tables_unbind_set(ctx, priv->set, &priv->binding);
++}
++
+ static void nft_dynset_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
+ {
+ struct nft_dynset *priv = nft_expr_priv(expr);
+
+- nf_tables_unbind_set(ctx, priv->set, &priv->binding);
+ if (priv->expr != NULL)
+ nft_expr_destroy(ctx, priv->expr);
++
++ nf_tables_destroy_set(ctx, priv->set);
+ }
+
+ static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
+@@ -279,6 +296,8 @@ static const struct nft_expr_ops nft_dynset_ops = {
+ .eval = nft_dynset_eval,
+ .init = nft_dynset_init,
+ .destroy = nft_dynset_destroy,
++ .activate = nft_dynset_activate,
++ .deactivate = nft_dynset_deactivate,
+ .dump = nft_dynset_dump,
+ };
+
+diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c
+index ad13e8643599..227b2b15a19c 100644
+--- a/net/netfilter/nft_lookup.c
++++ b/net/netfilter/nft_lookup.c
+@@ -121,12 +121,28 @@ static int nft_lookup_init(const struct nft_ctx *ctx,
+ return 0;
+ }
+
++static void nft_lookup_activate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ struct nft_lookup *priv = nft_expr_priv(expr);
++
++ nf_tables_rebind_set(ctx, priv->set, &priv->binding);
++}
++
++static void nft_lookup_deactivate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ struct nft_lookup *priv = nft_expr_priv(expr);
++
++ nf_tables_unbind_set(ctx, priv->set, &priv->binding);
++}
++
+ static void nft_lookup_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
+ {
+ struct nft_lookup *priv = nft_expr_priv(expr);
+
+- nf_tables_unbind_set(ctx, priv->set, &priv->binding);
++ nf_tables_destroy_set(ctx, priv->set);
+ }
+
+ static int nft_lookup_dump(struct sk_buff *skb, const struct nft_expr *expr)
+@@ -209,6 +225,8 @@ static const struct nft_expr_ops nft_lookup_ops = {
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)),
+ .eval = nft_lookup_eval,
+ .init = nft_lookup_init,
++ .activate = nft_lookup_activate,
++ .deactivate = nft_lookup_deactivate,
+ .destroy = nft_lookup_destroy,
+ .dump = nft_lookup_dump,
+ .validate = nft_lookup_validate,
+diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c
+index cdf348f751ec..a3185ca2a3a9 100644
+--- a/net/netfilter/nft_objref.c
++++ b/net/netfilter/nft_objref.c
+@@ -155,12 +155,28 @@ nla_put_failure:
+ return -1;
+ }
+
++static void nft_objref_map_activate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ struct nft_objref_map *priv = nft_expr_priv(expr);
++
++ nf_tables_rebind_set(ctx, priv->set, &priv->binding);
++}
++
++static void nft_objref_map_deactivate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ struct nft_objref_map *priv = nft_expr_priv(expr);
++
++ nf_tables_unbind_set(ctx, priv->set, &priv->binding);
++}
++
+ static void nft_objref_map_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
+ {
+ struct nft_objref_map *priv = nft_expr_priv(expr);
+
+- nf_tables_unbind_set(ctx, priv->set, &priv->binding);
++ nf_tables_destroy_set(ctx, priv->set);
+ }
+
+ static struct nft_expr_type nft_objref_type;
+@@ -169,6 +185,8 @@ static const struct nft_expr_ops nft_objref_map_ops = {
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)),
+ .eval = nft_objref_map_eval,
+ .init = nft_objref_map_init,
++ .activate = nft_objref_map_activate,
++ .deactivate = nft_objref_map_deactivate,
+ .destroy = nft_objref_map_destroy,
+ .dump = nft_objref_map_dump,
+ };
+--
+2.19.1
+
--- /dev/null
+From 81bfceedb55c7e59144b1a1b885f0bbf2ff17dda Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Sat, 2 Feb 2019 10:49:13 +0100
+Subject: netfilter: nf_tables: unbind set in rule from commit path
+
+Anonymous sets that are bound to rules from the same transaction trigger
+a kernel splat from the abort path due to double set list removal and
+double free.
+
+This patch updates the logic to search for the transaction that is
+responsible for creating the set and disable the set list removal and
+release, given the rule is now responsible for this. Lookup is reverse
+since the transaction that adds the set is likely to be at the tail of
+the list.
+
+Moreover, this patch adds the unbind step to deliver the event from the
+commit path. This should not be done from the worker thread, since we
+have no guarantees of in-order delivery to the listener.
+
+This patch removes the assumption that both activate and deactivate
+callbacks need to be provided.
+
+Fixes: cd5125d8f518 ("netfilter: nf_tables: split set destruction in deactivate and destroy phase")
+Reported-by: Mikhail Morfikov <mmorfikov@gmail.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ include/net/netfilter/nf_tables.h | 17 +++++--
+ net/netfilter/nf_tables_api.c | 85 +++++++++++++++----------------
+ net/netfilter/nft_compat.c | 6 ++-
+ net/netfilter/nft_dynset.c | 18 +++----
+ net/netfilter/nft_immediate.c | 6 ++-
+ net/netfilter/nft_lookup.c | 18 +++----
+ net/netfilter/nft_objref.c | 18 +++----
+ 7 files changed, 85 insertions(+), 83 deletions(-)
+
+diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
+index 2c33958f3e7a..50c101e0286a 100644
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -469,9 +469,7 @@ struct nft_set_binding {
+ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
+ struct nft_set_binding *binding);
+ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
+- struct nft_set_binding *binding);
+-void nf_tables_rebind_set(const struct nft_ctx *ctx, struct nft_set *set,
+- struct nft_set_binding *binding);
++ struct nft_set_binding *binding, bool commit);
+ void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set);
+
+ /**
+@@ -721,6 +719,13 @@ struct nft_expr_type {
+ #define NFT_EXPR_STATEFUL 0x1
+ #define NFT_EXPR_GC 0x2
+
++enum nft_trans_phase {
++ NFT_TRANS_PREPARE,
++ NFT_TRANS_ABORT,
++ NFT_TRANS_COMMIT,
++ NFT_TRANS_RELEASE
++};
++
+ /**
+ * struct nft_expr_ops - nf_tables expression operations
+ *
+@@ -750,7 +755,8 @@ struct nft_expr_ops {
+ void (*activate)(const struct nft_ctx *ctx,
+ const struct nft_expr *expr);
+ void (*deactivate)(const struct nft_ctx *ctx,
+- const struct nft_expr *expr);
++ const struct nft_expr *expr,
++ enum nft_trans_phase phase);
+ void (*destroy)(const struct nft_ctx *ctx,
+ const struct nft_expr *expr);
+ void (*destroy_clone)(const struct nft_ctx *ctx,
+@@ -1321,12 +1327,15 @@ struct nft_trans_rule {
+ struct nft_trans_set {
+ struct nft_set *set;
+ u32 set_id;
++ bool bound;
+ };
+
+ #define nft_trans_set(trans) \
+ (((struct nft_trans_set *)trans->data)->set)
+ #define nft_trans_set_id(trans) \
+ (((struct nft_trans_set *)trans->data)->set_id)
++#define nft_trans_set_bound(trans) \
++ (((struct nft_trans_set *)trans->data)->bound)
+
+ struct nft_trans_chain {
+ bool update;
+diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
+index dd2b28a09bd4..9f4d37b794eb 100644
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -112,6 +112,23 @@ static void nft_trans_destroy(struct nft_trans *trans)
+ kfree(trans);
+ }
+
++static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
++{
++ struct net *net = ctx->net;
++ struct nft_trans *trans;
++
++ if (!nft_set_is_anonymous(set))
++ return;
++
++ list_for_each_entry_reverse(trans, &net->nft.commit_list, list) {
++ if (trans->msg_type == NFT_MSG_NEWSET &&
++ nft_trans_set(trans) == set) {
++ nft_trans_set_bound(trans) = true;
++ break;
++ }
++ }
++}
++
+ static int nf_tables_register_hook(struct net *net,
+ const struct nft_table *table,
+ struct nft_chain *chain)
+@@ -207,18 +224,6 @@ static int nft_delchain(struct nft_ctx *ctx)
+ return err;
+ }
+
+-/* either expr ops provide both activate/deactivate, or neither */
+-static bool nft_expr_check_ops(const struct nft_expr_ops *ops)
+-{
+- if (!ops)
+- return true;
+-
+- if (WARN_ON_ONCE((!ops->activate ^ !ops->deactivate)))
+- return false;
+-
+- return true;
+-}
+-
+ static void nft_rule_expr_activate(const struct nft_ctx *ctx,
+ struct nft_rule *rule)
+ {
+@@ -234,14 +239,15 @@ static void nft_rule_expr_activate(const struct nft_ctx *ctx,
+ }
+
+ static void nft_rule_expr_deactivate(const struct nft_ctx *ctx,
+- struct nft_rule *rule)
++ struct nft_rule *rule,
++ enum nft_trans_phase phase)
+ {
+ 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->ops->deactivate(ctx, expr, phase);
+
+ expr = nft_expr_next(expr);
+ }
+@@ -292,7 +298,7 @@ static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule)
+ nft_trans_destroy(trans);
+ return err;
+ }
+- nft_rule_expr_deactivate(ctx, rule);
++ nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_PREPARE);
+
+ return 0;
+ }
+@@ -1926,9 +1932,6 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk,
+ */
+ int nft_register_expr(struct nft_expr_type *type)
+ {
+- if (!nft_expr_check_ops(type->ops))
+- return -EINVAL;
+-
+ nfnl_lock(NFNL_SUBSYS_NFTABLES);
+ if (type->family == NFPROTO_UNSPEC)
+ list_add_tail_rcu(&type->list, &nf_tables_expressions);
+@@ -2076,10 +2079,6 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx,
+ err = PTR_ERR(ops);
+ goto err1;
+ }
+- if (!nft_expr_check_ops(ops)) {
+- err = -EINVAL;
+- goto err1;
+- }
+ } else
+ ops = type->ops;
+
+@@ -2477,7 +2476,7 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
+ static void nf_tables_rule_release(const struct nft_ctx *ctx,
+ struct nft_rule *rule)
+ {
+- nft_rule_expr_deactivate(ctx, rule);
++ nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE);
+ nf_tables_rule_destroy(ctx, rule);
+ }
+
+@@ -3677,39 +3676,30 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
+ bind:
+ binding->chain = ctx->chain;
+ list_add_tail_rcu(&binding->list, &set->bindings);
++ nft_set_trans_bind(ctx, set);
++
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(nf_tables_bind_set);
+
+-void nf_tables_rebind_set(const struct nft_ctx *ctx, struct nft_set *set,
+- struct nft_set_binding *binding)
+-{
+- if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
+- nft_is_active(ctx->net, set))
+- list_add_tail_rcu(&set->list, &ctx->table->sets);
+-
+- list_add_tail_rcu(&binding->list, &set->bindings);
+-}
+-EXPORT_SYMBOL_GPL(nf_tables_rebind_set);
+-
+ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
+- struct nft_set_binding *binding)
++ struct nft_set_binding *binding, bool event)
+ {
+ list_del_rcu(&binding->list);
+
+- if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
+- nft_is_active(ctx->net, set))
++ if (list_empty(&set->bindings) && nft_set_is_anonymous(set)) {
+ list_del_rcu(&set->list);
++ if (event)
++ nf_tables_set_notify(ctx, set, NFT_MSG_DELSET,
++ GFP_KERNEL);
++ }
+ }
+ EXPORT_SYMBOL_GPL(nf_tables_unbind_set);
+
+ void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set)
+ {
+- if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
+- nft_is_active(ctx->net, set)) {
+- nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
++ if (list_empty(&set->bindings) && nft_set_is_anonymous(set))
+ nft_set_destroy(set);
+- }
+ }
+ EXPORT_SYMBOL_GPL(nf_tables_destroy_set);
+
+@@ -6462,6 +6452,9 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
+ nf_tables_rule_notify(&trans->ctx,
+ nft_trans_rule(trans),
+ NFT_MSG_DELRULE);
++ nft_rule_expr_deactivate(&trans->ctx,
++ nft_trans_rule(trans),
++ NFT_TRANS_COMMIT);
+ break;
+ case NFT_MSG_NEWSET:
+ nft_clear(net, nft_trans_set(trans));
+@@ -6549,7 +6542,8 @@ static void nf_tables_abort_release(struct nft_trans *trans)
+ nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
+ break;
+ case NFT_MSG_NEWSET:
+- nft_set_destroy(nft_trans_set(trans));
++ if (!nft_trans_set_bound(trans))
++ nft_set_destroy(nft_trans_set(trans));
+ break;
+ case NFT_MSG_NEWSETELEM:
+ nft_set_elem_destroy(nft_trans_elem_set(trans),
+@@ -6610,7 +6604,9 @@ static int __nf_tables_abort(struct net *net)
+ 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));
++ nft_rule_expr_deactivate(&trans->ctx,
++ nft_trans_rule(trans),
++ NFT_TRANS_ABORT);
+ break;
+ case NFT_MSG_DELRULE:
+ trans->ctx.chain->use++;
+@@ -6620,7 +6616,8 @@ static int __nf_tables_abort(struct net *net)
+ break;
+ case NFT_MSG_NEWSET:
+ trans->ctx.table->use--;
+- list_del_rcu(&nft_trans_set(trans)->list);
++ if (!nft_trans_set_bound(trans))
++ list_del_rcu(&nft_trans_set(trans)->list);
+ break;
+ case NFT_MSG_DELSET:
+ trans->ctx.table->use++;
+diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
+index 432139b7fa1f..12a95eec9565 100644
+--- a/net/netfilter/nft_compat.c
++++ b/net/netfilter/nft_compat.c
+@@ -569,10 +569,14 @@ static void nft_compat_activate_tg(const struct nft_ctx *ctx,
+ }
+
+ static void nft_compat_deactivate(const struct nft_ctx *ctx,
+- const struct nft_expr *expr)
++ const struct nft_expr *expr,
++ enum nft_trans_phase phase)
+ {
+ struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);
+
++ if (phase == NFT_TRANS_COMMIT)
++ return;
++
+ if (--xt->listcnt == 0)
+ list_del_init(&xt->head);
+ }
+diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
+index 07d4efd3d851..f1172f99752b 100644
+--- a/net/netfilter/nft_dynset.c
++++ b/net/netfilter/nft_dynset.c
+@@ -235,20 +235,17 @@ err1:
+ return err;
+ }
+
+-static void nft_dynset_activate(const struct nft_ctx *ctx,
+- const struct nft_expr *expr)
+-{
+- struct nft_dynset *priv = nft_expr_priv(expr);
+-
+- nf_tables_rebind_set(ctx, priv->set, &priv->binding);
+-}
+-
+ static void nft_dynset_deactivate(const struct nft_ctx *ctx,
+- const struct nft_expr *expr)
++ const struct nft_expr *expr,
++ enum nft_trans_phase phase)
+ {
+ struct nft_dynset *priv = nft_expr_priv(expr);
+
+- nf_tables_unbind_set(ctx, priv->set, &priv->binding);
++ if (phase == NFT_TRANS_PREPARE)
++ return;
++
++ nf_tables_unbind_set(ctx, priv->set, &priv->binding,
++ phase == NFT_TRANS_COMMIT);
+ }
+
+ static void nft_dynset_destroy(const struct nft_ctx *ctx,
+@@ -296,7 +293,6 @@ static const struct nft_expr_ops nft_dynset_ops = {
+ .eval = nft_dynset_eval,
+ .init = nft_dynset_init,
+ .destroy = nft_dynset_destroy,
+- .activate = nft_dynset_activate,
+ .deactivate = nft_dynset_deactivate,
+ .dump = nft_dynset_dump,
+ };
+diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c
+index 0777a93211e2..3f6d1d2a6281 100644
+--- a/net/netfilter/nft_immediate.c
++++ b/net/netfilter/nft_immediate.c
+@@ -72,10 +72,14 @@ static void nft_immediate_activate(const struct nft_ctx *ctx,
+ }
+
+ static void nft_immediate_deactivate(const struct nft_ctx *ctx,
+- const struct nft_expr *expr)
++ const struct nft_expr *expr,
++ enum nft_trans_phase phase)
+ {
+ const struct nft_immediate_expr *priv = nft_expr_priv(expr);
+
++ if (phase == NFT_TRANS_COMMIT)
++ return;
++
+ return nft_data_release(&priv->data, nft_dreg_to_type(priv->dreg));
+ }
+
+diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c
+index 227b2b15a19c..14496da5141d 100644
+--- a/net/netfilter/nft_lookup.c
++++ b/net/netfilter/nft_lookup.c
+@@ -121,20 +121,17 @@ static int nft_lookup_init(const struct nft_ctx *ctx,
+ return 0;
+ }
+
+-static void nft_lookup_activate(const struct nft_ctx *ctx,
+- const struct nft_expr *expr)
+-{
+- struct nft_lookup *priv = nft_expr_priv(expr);
+-
+- nf_tables_rebind_set(ctx, priv->set, &priv->binding);
+-}
+-
+ static void nft_lookup_deactivate(const struct nft_ctx *ctx,
+- const struct nft_expr *expr)
++ const struct nft_expr *expr,
++ enum nft_trans_phase phase)
+ {
+ struct nft_lookup *priv = nft_expr_priv(expr);
+
+- nf_tables_unbind_set(ctx, priv->set, &priv->binding);
++ if (phase == NFT_TRANS_PREPARE)
++ return;
++
++ nf_tables_unbind_set(ctx, priv->set, &priv->binding,
++ phase == NFT_TRANS_COMMIT);
+ }
+
+ static void nft_lookup_destroy(const struct nft_ctx *ctx,
+@@ -225,7 +222,6 @@ static const struct nft_expr_ops nft_lookup_ops = {
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)),
+ .eval = nft_lookup_eval,
+ .init = nft_lookup_init,
+- .activate = nft_lookup_activate,
+ .deactivate = nft_lookup_deactivate,
+ .destroy = nft_lookup_destroy,
+ .dump = nft_lookup_dump,
+diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c
+index a3185ca2a3a9..ae178e914486 100644
+--- a/net/netfilter/nft_objref.c
++++ b/net/netfilter/nft_objref.c
+@@ -155,20 +155,17 @@ nla_put_failure:
+ return -1;
+ }
+
+-static void nft_objref_map_activate(const struct nft_ctx *ctx,
+- const struct nft_expr *expr)
+-{
+- struct nft_objref_map *priv = nft_expr_priv(expr);
+-
+- nf_tables_rebind_set(ctx, priv->set, &priv->binding);
+-}
+-
+ static void nft_objref_map_deactivate(const struct nft_ctx *ctx,
+- const struct nft_expr *expr)
++ const struct nft_expr *expr,
++ enum nft_trans_phase phase)
+ {
+ struct nft_objref_map *priv = nft_expr_priv(expr);
+
+- nf_tables_unbind_set(ctx, priv->set, &priv->binding);
++ if (phase == NFT_TRANS_PREPARE)
++ return;
++
++ nf_tables_unbind_set(ctx, priv->set, &priv->binding,
++ phase == NFT_TRANS_COMMIT);
+ }
+
+ static void nft_objref_map_destroy(const struct nft_ctx *ctx,
+@@ -185,7 +182,6 @@ static const struct nft_expr_ops nft_objref_map_ops = {
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)),
+ .eval = nft_objref_map_eval,
+ .init = nft_objref_map_init,
+- .activate = nft_objref_map_activate,
+ .deactivate = nft_objref_map_deactivate,
+ .destroy = nft_objref_map_destroy,
+ .dump = nft_objref_map_dump,
+--
+2.19.1
+
--- /dev/null
+From 760cc71ec9c5a078e633cebbb40f6430771b4532 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Thu, 30 Aug 2018 10:42:55 +0200
+Subject: netfilter: nf_tables: warn when expr implements only one of
+ activate/deactivate
+
+->destroy is only allowed to free data, or do other cleanups that do not
+have side effects on other state, such as visibility to other netlink
+requests.
+
+Such things need to be done in ->deactivate.
+As a transaction can fail, we need to make sure we can undo such
+operations, therefore ->activate() has to be provided too.
+
+So print a warning and refuse registration if expr->ops provides
+only one of the two operations.
+
+v2: fix nft_expr_check_ops to not repeat same check twice (Jones Desougi)
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ net/netfilter/nf_tables_api.c | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
+index 667f6eccbec7..dd2b28a09bd4 100644
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -207,6 +207,18 @@ static int nft_delchain(struct nft_ctx *ctx)
+ return err;
+ }
+
++/* either expr ops provide both activate/deactivate, or neither */
++static bool nft_expr_check_ops(const struct nft_expr_ops *ops)
++{
++ if (!ops)
++ return true;
++
++ if (WARN_ON_ONCE((!ops->activate ^ !ops->deactivate)))
++ return false;
++
++ return true;
++}
++
+ static void nft_rule_expr_activate(const struct nft_ctx *ctx,
+ struct nft_rule *rule)
+ {
+@@ -1914,6 +1926,9 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk,
+ */
+ int nft_register_expr(struct nft_expr_type *type)
+ {
++ if (!nft_expr_check_ops(type->ops))
++ return -EINVAL;
++
+ nfnl_lock(NFNL_SUBSYS_NFTABLES);
+ if (type->family == NFPROTO_UNSPEC)
+ list_add_tail_rcu(&type->list, &nf_tables_expressions);
+@@ -2061,6 +2076,10 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx,
+ err = PTR_ERR(ops);
+ goto err1;
+ }
++ if (!nft_expr_check_ops(ops)) {
++ err = -EINVAL;
++ goto err1;
++ }
+ } else
+ ops = type->ops;
+
+--
+2.19.1
+
--- /dev/null
+From 05bfe7741101dbec96b27d75de4e22b1b367fe6c Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Mon, 14 Jan 2019 14:28:50 +0100
+Subject: netfilter: nft_compat: destroy function must not have side effects
+
+The nft_compat destroy function deletes the nft_xt object from a list.
+This isn't allowed anymore. Destroy functions are called asynchronously,
+i.e. next batch can find the object that has a pending ->destroy()
+invocation:
+
+cpu0 cpu1
+ worker
+ ->destroy for_each_entry()
+ if (x == ...
+ return x->ops;
+ list_del(x)
+ kfree_rcu(x)
+ expr->ops->... // ops was free'd
+
+To resolve this, the list_del needs to occur before the transaction
+mutex gets released. nf_tables has a 'deactivate' hook for this
+purpose, so use that to unlink the object from the list.
+
+Fixes: 0935d5588400 ("netfilter: nf_tables: asynchronous release")
+Reported-by: Taehee Yoo <ap420073@gmail.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ net/netfilter/nft_compat.c | 48 +++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 47 insertions(+), 1 deletion(-)
+
+diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
+index 61c098555507..432139b7fa1f 100644
+--- a/net/netfilter/nft_compat.c
++++ b/net/netfilter/nft_compat.c
+@@ -29,6 +29,9 @@ struct nft_xt {
+ struct nft_expr_ops ops;
+ refcount_t refcnt;
+
++ /* used only when transaction mutex is locked */
++ unsigned int listcnt;
++
+ /* 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
+@@ -61,7 +64,7 @@ static struct nft_compat_net *nft_compat_pernet(struct net *net)
+ static bool nft_xt_put(struct nft_xt *xt)
+ {
+ if (refcount_dec_and_test(&xt->refcnt)) {
+- list_del(&xt->head);
++ WARN_ON_ONCE(!list_empty(&xt->head));
+ kfree_rcu(xt, rcu_head);
+ return true;
+ }
+@@ -537,6 +540,43 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
+ __nft_match_destroy(ctx, expr, nft_expr_priv(expr));
+ }
+
++static void nft_compat_activate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr,
++ struct list_head *h)
++{
++ struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);
++
++ if (xt->listcnt == 0)
++ list_add(&xt->head, h);
++
++ xt->listcnt++;
++}
++
++static void nft_compat_activate_mt(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ struct nft_compat_net *cn = nft_compat_pernet(ctx->net);
++
++ nft_compat_activate(ctx, expr, &cn->nft_match_list);
++}
++
++static void nft_compat_activate_tg(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ struct nft_compat_net *cn = nft_compat_pernet(ctx->net);
++
++ nft_compat_activate(ctx, expr, &cn->nft_target_list);
++}
++
++static void nft_compat_deactivate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);
++
++ if (--xt->listcnt == 0)
++ list_del_init(&xt->head);
++}
++
+ static void
+ nft_match_large_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
+ {
+@@ -789,6 +829,8 @@ nft_match_select_ops(const struct nft_ctx *ctx,
+ nft_match->ops.eval = nft_match_eval;
+ nft_match->ops.init = nft_match_init;
+ nft_match->ops.destroy = nft_match_destroy;
++ nft_match->ops.activate = nft_compat_activate_mt;
++ nft_match->ops.deactivate = nft_compat_deactivate;
+ nft_match->ops.dump = nft_match_dump;
+ nft_match->ops.validate = nft_match_validate;
+ nft_match->ops.data = match;
+@@ -805,6 +847,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,
+
+ nft_match->ops.size = matchsize;
+
++ nft_match->listcnt = 1;
+ list_add(&nft_match->head, &cn->nft_match_list);
+
+ return &nft_match->ops;
+@@ -891,6 +934,8 @@ nft_target_select_ops(const struct nft_ctx *ctx,
+ nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
+ nft_target->ops.init = nft_target_init;
+ nft_target->ops.destroy = nft_target_destroy;
++ nft_target->ops.activate = nft_compat_activate_tg;
++ nft_target->ops.deactivate = nft_compat_deactivate;
+ nft_target->ops.dump = nft_target_dump;
+ nft_target->ops.validate = nft_target_validate;
+ nft_target->ops.data = target;
+@@ -900,6 +945,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
+ else
+ nft_target->ops.eval = nft_target_eval_xt;
+
++ nft_target->listcnt = 1;
+ list_add(&nft_target->head, &cn->nft_target_list);
+
+ return &nft_target->ops;
+--
+2.19.1
+
--- /dev/null
+From 3e6101cb326f2b53356dbc4c4fee3dc7c805399c Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Tue, 5 Feb 2019 12:16:18 +0100
+Subject: netfilter: nft_compat: don't use refcount_inc on newly allocated
+ entry
+
+[ Upstream commit 947e492c0fc2132ae5fca081a9c2952ccaab0404 ]
+
+When I moved the refcount to refcount_t type I missed the fact that
+refcount_inc() will result in use-after-free warning with
+CONFIG_REFCOUNT_FULL=y builds.
+
+The correct fix would be to init the reference count to 1 at allocation
+time, but, unfortunately we cannot do this, as we can't undo that
+in case something else fails later in the batch.
+
+So only solution I see is to special-case the 'new entry' condition
+and replace refcount_inc() with a "delayed" refcount_set(1) in this case,
+as done here.
+
+The .activate callback can be removed to simplify things, we only
+need to make sure that deactivate() decrements/unlinks the entry
+from the list at end of transaction phase (commit or abort).
+
+Fixes: 12c44aba6618 ("netfilter: nft_compat: use refcnt_t type for nft_xt reference count")
+Reported-by: Jordan Glover <Golden_Miller83@protonmail.ch>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nft_compat.c | 62 ++++++++++++++------------------------
+ 1 file changed, 23 insertions(+), 39 deletions(-)
+
+diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
+index 12a95eec9565..859eb3e12ddf 100644
+--- a/net/netfilter/nft_compat.c
++++ b/net/netfilter/nft_compat.c
+@@ -61,6 +61,21 @@ static struct nft_compat_net *nft_compat_pernet(struct net *net)
+ return net_generic(net, nft_compat_net_id);
+ }
+
++static void nft_xt_get(struct nft_xt *xt)
++{
++ /* refcount_inc() warns on 0 -> 1 transition, but we can't
++ * init the reference count to 1 in .select_ops -- we can't
++ * undo such an increase when another expression inside the same
++ * rule fails afterwards.
++ */
++ if (xt->listcnt == 0)
++ refcount_set(&xt->refcnt, 1);
++ else
++ refcount_inc(&xt->refcnt);
++
++ xt->listcnt++;
++}
++
+ static bool nft_xt_put(struct nft_xt *xt)
+ {
+ if (refcount_dec_and_test(&xt->refcnt)) {
+@@ -291,7 +306,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+ return -EINVAL;
+
+ nft_xt = container_of(expr->ops, struct nft_xt, ops);
+- refcount_inc(&nft_xt->refcnt);
++ nft_xt_get(nft_xt);
+ return 0;
+ }
+
+@@ -486,7 +501,7 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+ return ret;
+
+ nft_xt = container_of(expr->ops, struct nft_xt, ops);
+- refcount_inc(&nft_xt->refcnt);
++ nft_xt_get(nft_xt);
+ return 0;
+ }
+
+@@ -540,45 +555,16 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
+ __nft_match_destroy(ctx, expr, nft_expr_priv(expr));
+ }
+
+-static void nft_compat_activate(const struct nft_ctx *ctx,
+- const struct nft_expr *expr,
+- struct list_head *h)
+-{
+- struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);
+-
+- if (xt->listcnt == 0)
+- list_add(&xt->head, h);
+-
+- xt->listcnt++;
+-}
+-
+-static void nft_compat_activate_mt(const struct nft_ctx *ctx,
+- const struct nft_expr *expr)
+-{
+- struct nft_compat_net *cn = nft_compat_pernet(ctx->net);
+-
+- nft_compat_activate(ctx, expr, &cn->nft_match_list);
+-}
+-
+-static void nft_compat_activate_tg(const struct nft_ctx *ctx,
+- const struct nft_expr *expr)
+-{
+- struct nft_compat_net *cn = nft_compat_pernet(ctx->net);
+-
+- nft_compat_activate(ctx, expr, &cn->nft_target_list);
+-}
+-
+ static void nft_compat_deactivate(const struct nft_ctx *ctx,
+ const struct nft_expr *expr,
+ enum nft_trans_phase phase)
+ {
+ struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);
+
+- if (phase == NFT_TRANS_COMMIT)
+- return;
+-
+- if (--xt->listcnt == 0)
+- list_del_init(&xt->head);
++ if (phase == NFT_TRANS_ABORT || phase == NFT_TRANS_COMMIT) {
++ if (--xt->listcnt == 0)
++ list_del_init(&xt->head);
++ }
+ }
+
+ static void
+@@ -833,7 +819,6 @@ nft_match_select_ops(const struct nft_ctx *ctx,
+ nft_match->ops.eval = nft_match_eval;
+ nft_match->ops.init = nft_match_init;
+ nft_match->ops.destroy = nft_match_destroy;
+- nft_match->ops.activate = nft_compat_activate_mt;
+ nft_match->ops.deactivate = nft_compat_deactivate;
+ nft_match->ops.dump = nft_match_dump;
+ nft_match->ops.validate = nft_match_validate;
+@@ -851,7 +836,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,
+
+ nft_match->ops.size = matchsize;
+
+- nft_match->listcnt = 1;
++ nft_match->listcnt = 0;
+ list_add(&nft_match->head, &cn->nft_match_list);
+
+ return &nft_match->ops;
+@@ -938,7 +923,6 @@ nft_target_select_ops(const struct nft_ctx *ctx,
+ nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
+ nft_target->ops.init = nft_target_init;
+ nft_target->ops.destroy = nft_target_destroy;
+- nft_target->ops.activate = nft_compat_activate_tg;
+ nft_target->ops.deactivate = nft_compat_deactivate;
+ nft_target->ops.dump = nft_target_dump;
+ nft_target->ops.validate = nft_target_validate;
+@@ -949,7 +933,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
+ else
+ nft_target->ops.eval = nft_target_eval_xt;
+
+- nft_target->listcnt = 1;
++ nft_target->listcnt = 0;
+ list_add(&nft_target->head, &cn->nft_target_list);
+
+ return &nft_target->ops;
+--
+2.19.1
+
--- /dev/null
+From 26dcae77515ded6380a396fcd6834cd41cdb1764 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Mon, 14 Jan 2019 14:28:49 +0100
+Subject: netfilter: nft_compat: make lists per netns
+
+[ Upstream commit cf52572ebbd7189a1966c2b5fc34b97078cd1dce ]
+
+There are two problems with nft_compat since the netlink config
+plane uses a per-netns mutex:
+
+1. Concurrent add/del accesses to the same list
+2. accesses to a list element after it has been free'd already.
+
+This patch fixes the first problem.
+
+Freeing occurs from a work queue, after transaction mutexes have been
+released, i.e., it still possible for a new transaction (even from
+same net ns) to find the to-be-deleted expression in the list.
+
+The ->destroy functions are not allowed to have any such side effects,
+i.e. the list_del() in the destroy function is not allowed.
+
+This part of the problem is solved in the next patch.
+I tried to make this work by serializing list access via mutex
+and by moving list_del() to a deactivate callback, but
+Taehee spotted following race on this approach:
+
+ NET #0 NET #1
+ >select_ops()
+ ->init()
+ ->select_ops()
+ ->deactivate()
+ ->destroy()
+ nft_xt_put()
+ kfree_rcu(xt, rcu_head);
+ ->init() <-- use-after-free occurred.
+
+Unfortunately, we can't increment reference count in
+select_ops(), because we can't undo the refcount increase in
+case a different expression fails in the same batch.
+
+(The destroy hook will only be called in case the expression
+ was initialized successfully).
+
+Fixes: f102d66b335a ("netfilter: nf_tables: use dedicated mutex to guard transactions")
+Reported-by: Taehee Yoo <ap420073@gmail.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nft_compat.c | 129 +++++++++++++++++++++++++------------
+ 1 file changed, 89 insertions(+), 40 deletions(-)
+
+diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
+index 24ec9552e126..61c098555507 100644
+--- a/net/netfilter/nft_compat.c
++++ b/net/netfilter/nft_compat.c
+@@ -22,6 +22,7 @@
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/netfilter_arp/arp_tables.h>
+ #include <net/netfilter/nf_tables.h>
++#include <net/netns/generic.h>
+
+ struct nft_xt {
+ struct list_head head;
+@@ -43,6 +44,20 @@ struct nft_xt_match_priv {
+ void *info;
+ };
+
++struct nft_compat_net {
++ struct list_head nft_target_list;
++ struct list_head nft_match_list;
++};
++
++static unsigned int nft_compat_net_id __read_mostly;
++static struct nft_expr_type nft_match_type;
++static struct nft_expr_type nft_target_type;
++
++static struct nft_compat_net *nft_compat_pernet(struct net *net)
++{
++ return net_generic(net, nft_compat_net_id);
++}
++
+ static bool nft_xt_put(struct nft_xt *xt)
+ {
+ if (refcount_dec_and_test(&xt->refcnt)) {
+@@ -715,10 +730,6 @@ static const struct nfnetlink_subsystem nfnl_compat_subsys = {
+ .cb = nfnl_nft_compat_cb,
+ };
+
+-static LIST_HEAD(nft_match_list);
+-
+-static struct nft_expr_type nft_match_type;
+-
+ static bool nft_match_cmp(const struct xt_match *match,
+ const char *name, u32 rev, u32 family)
+ {
+@@ -730,6 +741,7 @@ static const struct nft_expr_ops *
+ nft_match_select_ops(const struct nft_ctx *ctx,
+ const struct nlattr * const tb[])
+ {
++ struct nft_compat_net *cn;
+ struct nft_xt *nft_match;
+ struct xt_match *match;
+ unsigned int matchsize;
+@@ -746,8 +758,10 @@ nft_match_select_ops(const struct nft_ctx *ctx,
+ rev = ntohl(nla_get_be32(tb[NFTA_MATCH_REV]));
+ family = ctx->family;
+
++ cn = nft_compat_pernet(ctx->net);
++
+ /* Re-use the existing match if it's already loaded. */
+- list_for_each_entry(nft_match, &nft_match_list, head) {
++ list_for_each_entry(nft_match, &cn->nft_match_list, head) {
+ struct xt_match *match = nft_match->ops.data;
+
+ if (nft_match_cmp(match, mt_name, rev, family))
+@@ -791,7 +805,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,
+
+ nft_match->ops.size = matchsize;
+
+- list_add(&nft_match->head, &nft_match_list);
++ list_add(&nft_match->head, &cn->nft_match_list);
+
+ return &nft_match->ops;
+ err:
+@@ -807,10 +821,6 @@ static struct nft_expr_type nft_match_type __read_mostly = {
+ .owner = THIS_MODULE,
+ };
+
+-static LIST_HEAD(nft_target_list);
+-
+-static struct nft_expr_type nft_target_type;
+-
+ static bool nft_target_cmp(const struct xt_target *tg,
+ const char *name, u32 rev, u32 family)
+ {
+@@ -822,6 +832,7 @@ static const struct nft_expr_ops *
+ nft_target_select_ops(const struct nft_ctx *ctx,
+ const struct nlattr * const tb[])
+ {
++ struct nft_compat_net *cn;
+ struct nft_xt *nft_target;
+ struct xt_target *target;
+ char *tg_name;
+@@ -842,8 +853,9 @@ nft_target_select_ops(const struct nft_ctx *ctx,
+ strcmp(tg_name, "standard") == 0)
+ return ERR_PTR(-EINVAL);
+
++ cn = nft_compat_pernet(ctx->net);
+ /* Re-use the existing target if it's already loaded. */
+- list_for_each_entry(nft_target, &nft_target_list, head) {
++ list_for_each_entry(nft_target, &cn->nft_target_list, head) {
+ struct xt_target *target = nft_target->ops.data;
+
+ if (!target->target)
+@@ -888,7 +900,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
+ else
+ nft_target->ops.eval = nft_target_eval_xt;
+
+- list_add(&nft_target->head, &nft_target_list);
++ list_add(&nft_target->head, &cn->nft_target_list);
+
+ return &nft_target->ops;
+ err:
+@@ -904,13 +916,74 @@ static struct nft_expr_type nft_target_type __read_mostly = {
+ .owner = THIS_MODULE,
+ };
+
++static int __net_init nft_compat_init_net(struct net *net)
++{
++ struct nft_compat_net *cn = nft_compat_pernet(net);
++
++ INIT_LIST_HEAD(&cn->nft_target_list);
++ INIT_LIST_HEAD(&cn->nft_match_list);
++
++ return 0;
++}
++
++static void __net_exit nft_compat_exit_net(struct net *net)
++{
++ struct nft_compat_net *cn = nft_compat_pernet(net);
++ struct nft_xt *xt, *next;
++
++ if (list_empty(&cn->nft_match_list) &&
++ list_empty(&cn->nft_target_list))
++ return;
++
++ /* If there was an error that caused nft_xt expr to not be initialized
++ * fully and noone else requested the same expression later, the lists
++ * contain 0-refcount entries that still hold module reference.
++ *
++ * Clean them here.
++ */
++ mutex_lock(&net->nft.commit_mutex);
++ list_for_each_entry_safe(xt, next, &cn->nft_target_list, head) {
++ struct xt_target *target = xt->ops.data;
++
++ list_del_init(&xt->head);
++
++ if (refcount_read(&xt->refcnt))
++ continue;
++ module_put(target->me);
++ kfree(xt);
++ }
++
++ list_for_each_entry_safe(xt, next, &cn->nft_match_list, head) {
++ struct xt_match *match = xt->ops.data;
++
++ list_del_init(&xt->head);
++
++ if (refcount_read(&xt->refcnt))
++ continue;
++ module_put(match->me);
++ kfree(xt);
++ }
++ mutex_unlock(&net->nft.commit_mutex);
++}
++
++static struct pernet_operations nft_compat_net_ops = {
++ .init = nft_compat_init_net,
++ .exit = nft_compat_exit_net,
++ .id = &nft_compat_net_id,
++ .size = sizeof(struct nft_compat_net),
++};
++
+ static int __init nft_compat_module_init(void)
+ {
+ int ret;
+
++ ret = register_pernet_subsys(&nft_compat_net_ops);
++ if (ret < 0)
++ goto err_target;
++
+ ret = nft_register_expr(&nft_match_type);
+ if (ret < 0)
+- return ret;
++ goto err_pernet;
+
+ ret = nft_register_expr(&nft_target_type);
+ if (ret < 0)
+@@ -923,45 +996,21 @@ static int __init nft_compat_module_init(void)
+ }
+
+ return ret;
+-
+ err_target:
+ nft_unregister_expr(&nft_target_type);
+ err_match:
+ nft_unregister_expr(&nft_match_type);
++err_pernet:
++ unregister_pernet_subsys(&nft_compat_net_ops);
+ return ret;
+ }
+
+ 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(refcount_read(&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(refcount_read(&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);
++ unregister_pernet_subsys(&nft_compat_net_ops);
+ }
+
+ MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFT_COMPAT);
+--
+2.19.1
+
--- /dev/null
+From 42df9defd5f071d978dd3db8c21adf2ff7b2b46d Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Wed, 13 Feb 2019 13:18:36 +0100
+Subject: netfilter: nft_compat: use .release_ops and remove list of extension
+
+[ Upstream commit b8e204006340b7aaf32bd2b9806c692f6e0cb38a ]
+
+Add .release_ops, that is called in case of error at a later stage in
+the expression initialization path, ie. .select_ops() has been already
+set up operations and that needs to be undone. This allows us to unwind
+.select_ops from the error path, ie. release the dynamic operations for
+this extension.
+
+Moreover, allocate one single operation instead of recycling them, this
+comes at the cost of consuming a bit more memory per rule, but it
+simplifies the infrastructure.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/netfilter/nf_tables.h | 3 +
+ net/netfilter/nf_tables_api.c | 7 +-
+ net/netfilter/nft_compat.c | 281 ++++++------------------------
+ 3 files changed, 64 insertions(+), 227 deletions(-)
+
+diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
+index 50c101e0286a..f66bb406004b 100644
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -690,10 +690,12 @@ static inline void nft_set_gc_batch_add(struct nft_set_gc_batch *gcb,
+ gcb->elems[gcb->head.cnt++] = elem;
+ }
+
++struct nft_expr_ops;
+ /**
+ * struct nft_expr_type - nf_tables expression type
+ *
+ * @select_ops: function to select nft_expr_ops
++ * @release_ops: release nft_expr_ops
+ * @ops: default ops, used when no select_ops functions is present
+ * @list: used internally
+ * @name: Identifier
+@@ -706,6 +708,7 @@ static inline void nft_set_gc_batch_add(struct nft_set_gc_batch *gcb,
+ struct nft_expr_type {
+ const struct nft_expr_ops *(*select_ops)(const struct nft_ctx *,
+ const struct nlattr * const tb[]);
++ void (*release_ops)(const struct nft_expr_ops *ops);
+ const struct nft_expr_ops *ops;
+ struct list_head list;
+ const char *name;
+diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
+index 9f4d37b794eb..de5908d51758 100644
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -2123,6 +2123,7 @@ struct nft_expr *nft_expr_init(const struct nft_ctx *ctx,
+ {
+ struct nft_expr_info info;
+ struct nft_expr *expr;
++ struct module *owner;
+ int err;
+
+ err = nf_tables_expr_parse(ctx, nla, &info);
+@@ -2142,7 +2143,11 @@ struct nft_expr *nft_expr_init(const struct nft_ctx *ctx,
+ err3:
+ kfree(expr);
+ err2:
+- module_put(info.ops->type->owner);
++ owner = info.ops->type->owner;
++ if (info.ops->type->release_ops)
++ info.ops->type->release_ops(info.ops);
++
++ module_put(owner);
+ err1:
+ return ERR_PTR(err);
+ }
+diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
+index 859eb3e12ddf..1245e02239d9 100644
+--- a/net/netfilter/nft_compat.c
++++ b/net/netfilter/nft_compat.c
+@@ -22,23 +22,6 @@
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/netfilter_arp/arp_tables.h>
+ #include <net/netfilter/nf_tables.h>
+-#include <net/netns/generic.h>
+-
+-struct nft_xt {
+- struct list_head head;
+- struct nft_expr_ops ops;
+- refcount_t refcnt;
+-
+- /* used only when transaction mutex is locked */
+- unsigned int listcnt;
+-
+- /* 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;
+-};
+
+ /* Used for matches where *info is larger than X byte */
+ #define NFT_MATCH_LARGE_THRESH 192
+@@ -47,46 +30,6 @@ struct nft_xt_match_priv {
+ void *info;
+ };
+
+-struct nft_compat_net {
+- struct list_head nft_target_list;
+- struct list_head nft_match_list;
+-};
+-
+-static unsigned int nft_compat_net_id __read_mostly;
+-static struct nft_expr_type nft_match_type;
+-static struct nft_expr_type nft_target_type;
+-
+-static struct nft_compat_net *nft_compat_pernet(struct net *net)
+-{
+- return net_generic(net, nft_compat_net_id);
+-}
+-
+-static void nft_xt_get(struct nft_xt *xt)
+-{
+- /* refcount_inc() warns on 0 -> 1 transition, but we can't
+- * init the reference count to 1 in .select_ops -- we can't
+- * undo such an increase when another expression inside the same
+- * rule fails afterwards.
+- */
+- if (xt->listcnt == 0)
+- refcount_set(&xt->refcnt, 1);
+- else
+- refcount_inc(&xt->refcnt);
+-
+- xt->listcnt++;
+-}
+-
+-static bool nft_xt_put(struct nft_xt *xt)
+-{
+- if (refcount_dec_and_test(&xt->refcnt)) {
+- WARN_ON_ONCE(!list_empty(&xt->head));
+- kfree_rcu(xt, rcu_head);
+- return true;
+- }
+-
+- return false;
+-}
+-
+ static int nft_compat_chain_validate_dependency(const struct nft_ctx *ctx,
+ const char *tablename)
+ {
+@@ -281,7 +224,6 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+ 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 = {};
+@@ -305,8 +247,6 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+ if (!target->target)
+ return -EINVAL;
+
+- nft_xt = container_of(expr->ops, struct nft_xt, ops);
+- nft_xt_get(nft_xt);
+ return 0;
+ }
+
+@@ -325,8 +265,8 @@ nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
+ if (par.target->destroy != NULL)
+ par.target->destroy(&par);
+
+- if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops)))
+- module_put(me);
++ module_put(me);
++ kfree(expr->ops);
+ }
+
+ static int nft_target_dump(struct sk_buff *skb, const struct nft_expr *expr)
+@@ -480,7 +420,6 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+ 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 = {};
+@@ -496,13 +435,7 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+
+ nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);
+
+- ret = xt_check_match(&par, size, proto, inv);
+- if (ret < 0)
+- return ret;
+-
+- nft_xt = container_of(expr->ops, struct nft_xt, ops);
+- nft_xt_get(nft_xt);
+- return 0;
++ return xt_check_match(&par, size, proto, inv);
+ }
+
+ static int
+@@ -545,8 +478,8 @@ __nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr,
+ if (par.match->destroy != NULL)
+ par.match->destroy(&par);
+
+- if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops)))
+- module_put(me);
++ module_put(me);
++ kfree(expr->ops);
+ }
+
+ static void
+@@ -555,18 +488,6 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
+ __nft_match_destroy(ctx, expr, nft_expr_priv(expr));
+ }
+
+-static void nft_compat_deactivate(const struct nft_ctx *ctx,
+- const struct nft_expr *expr,
+- enum nft_trans_phase phase)
+-{
+- struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);
+-
+- if (phase == NFT_TRANS_ABORT || phase == NFT_TRANS_COMMIT) {
+- if (--xt->listcnt == 0)
+- list_del_init(&xt->head);
+- }
+-}
+-
+ static void
+ nft_match_large_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
+ {
+@@ -760,19 +681,13 @@ static const struct nfnetlink_subsystem nfnl_compat_subsys = {
+ .cb = nfnl_nft_compat_cb,
+ };
+
+-static bool nft_match_cmp(const struct xt_match *match,
+- const char *name, u32 rev, u32 family)
+-{
+- return strcmp(match->name, name) == 0 && match->revision == rev &&
+- (match->family == NFPROTO_UNSPEC || match->family == family);
+-}
++static struct nft_expr_type nft_match_type;
+
+ static const struct nft_expr_ops *
+ nft_match_select_ops(const struct nft_ctx *ctx,
+ const struct nlattr * const tb[])
+ {
+- struct nft_compat_net *cn;
+- struct nft_xt *nft_match;
++ struct nft_expr_ops *ops;
+ struct xt_match *match;
+ unsigned int matchsize;
+ char *mt_name;
+@@ -788,16 +703,6 @@ nft_match_select_ops(const struct nft_ctx *ctx,
+ rev = ntohl(nla_get_be32(tb[NFTA_MATCH_REV]));
+ family = ctx->family;
+
+- cn = nft_compat_pernet(ctx->net);
+-
+- /* Re-use the existing match if it's already loaded. */
+- list_for_each_entry(nft_match, &cn->nft_match_list, head) {
+- struct xt_match *match = nft_match->ops.data;
+-
+- if (nft_match_cmp(match, mt_name, rev, family))
+- return &nft_match->ops;
+- }
+-
+ match = xt_request_find_match(family, mt_name, rev);
+ if (IS_ERR(match))
+ return ERR_PTR(-ENOENT);
+@@ -807,65 +712,62 @@ nft_match_select_ops(const struct nft_ctx *ctx,
+ goto err;
+ }
+
+- /* This is the first time we use this match, allocate operations */
+- nft_match = kzalloc(sizeof(struct nft_xt), GFP_KERNEL);
+- if (nft_match == NULL) {
++ ops = kzalloc(sizeof(struct nft_expr_ops), GFP_KERNEL);
++ if (!ops) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+- refcount_set(&nft_match->refcnt, 0);
+- nft_match->ops.type = &nft_match_type;
+- nft_match->ops.eval = nft_match_eval;
+- nft_match->ops.init = nft_match_init;
+- nft_match->ops.destroy = nft_match_destroy;
+- nft_match->ops.deactivate = nft_compat_deactivate;
+- nft_match->ops.dump = nft_match_dump;
+- nft_match->ops.validate = nft_match_validate;
+- nft_match->ops.data = match;
++ ops->type = &nft_match_type;
++ ops->eval = nft_match_eval;
++ ops->init = nft_match_init;
++ ops->destroy = nft_match_destroy;
++ ops->dump = nft_match_dump;
++ ops->validate = nft_match_validate;
++ 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;
++ ops->eval = nft_match_large_eval;
++ ops->init = nft_match_large_init;
++ ops->destroy = nft_match_large_destroy;
++ ops->dump = nft_match_large_dump;
+ }
+
+- nft_match->ops.size = matchsize;
++ ops->size = matchsize;
+
+- nft_match->listcnt = 0;
+- list_add(&nft_match->head, &cn->nft_match_list);
+-
+- return &nft_match->ops;
++ return ops;
+ err:
+ module_put(match->me);
+ return ERR_PTR(err);
+ }
+
++static void nft_match_release_ops(const struct nft_expr_ops *ops)
++{
++ struct xt_match *match = ops->data;
++
++ module_put(match->me);
++ kfree(ops);
++}
++
+ static struct nft_expr_type nft_match_type __read_mostly = {
+ .name = "match",
+ .select_ops = nft_match_select_ops,
++ .release_ops = nft_match_release_ops,
+ .policy = nft_match_policy,
+ .maxattr = NFTA_MATCH_MAX,
+ .owner = THIS_MODULE,
+ };
+
+-static bool nft_target_cmp(const struct xt_target *tg,
+- const char *name, u32 rev, u32 family)
+-{
+- return strcmp(tg->name, name) == 0 && tg->revision == rev &&
+- (tg->family == NFPROTO_UNSPEC || tg->family == family);
+-}
++static struct nft_expr_type nft_target_type;
+
+ static const struct nft_expr_ops *
+ nft_target_select_ops(const struct nft_ctx *ctx,
+ const struct nlattr * const tb[])
+ {
+- struct nft_compat_net *cn;
+- struct nft_xt *nft_target;
++ struct nft_expr_ops *ops;
+ struct xt_target *target;
+ char *tg_name;
+ u32 rev, family;
+@@ -885,18 +787,6 @@ nft_target_select_ops(const struct nft_ctx *ctx,
+ strcmp(tg_name, "standard") == 0)
+ return ERR_PTR(-EINVAL);
+
+- cn = nft_compat_pernet(ctx->net);
+- /* Re-use the existing target if it's already loaded. */
+- list_for_each_entry(nft_target, &cn->nft_target_list, head) {
+- struct xt_target *target = nft_target->ops.data;
+-
+- if (!target->target)
+- continue;
+-
+- if (nft_target_cmp(target, tg_name, rev, family))
+- return &nft_target->ops;
+- }
+-
+ target = xt_request_find_target(family, tg_name, rev);
+ if (IS_ERR(target))
+ return ERR_PTR(-ENOENT);
+@@ -911,113 +801,55 @@ nft_target_select_ops(const struct nft_ctx *ctx,
+ goto err;
+ }
+
+- /* This is the first time we use this target, allocate operations */
+- nft_target = kzalloc(sizeof(struct nft_xt), GFP_KERNEL);
+- if (nft_target == NULL) {
++ ops = kzalloc(sizeof(struct nft_expr_ops), GFP_KERNEL);
++ if (!ops) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+- refcount_set(&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;
+- nft_target->ops.destroy = nft_target_destroy;
+- nft_target->ops.deactivate = nft_compat_deactivate;
+- nft_target->ops.dump = nft_target_dump;
+- nft_target->ops.validate = nft_target_validate;
+- nft_target->ops.data = target;
++ ops->type = &nft_target_type;
++ ops->size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
++ ops->init = nft_target_init;
++ ops->destroy = nft_target_destroy;
++ ops->dump = nft_target_dump;
++ ops->validate = nft_target_validate;
++ ops->data = target;
+
+ if (family == NFPROTO_BRIDGE)
+- nft_target->ops.eval = nft_target_eval_bridge;
++ ops->eval = nft_target_eval_bridge;
+ else
+- nft_target->ops.eval = nft_target_eval_xt;
+-
+- nft_target->listcnt = 0;
+- list_add(&nft_target->head, &cn->nft_target_list);
++ ops->eval = nft_target_eval_xt;
+
+- return &nft_target->ops;
++ return ops;
+ err:
+ module_put(target->me);
+ return ERR_PTR(err);
+ }
+
++static void nft_target_release_ops(const struct nft_expr_ops *ops)
++{
++ struct xt_target *target = ops->data;
++
++ module_put(target->me);
++ kfree(ops);
++}
++
+ static struct nft_expr_type nft_target_type __read_mostly = {
+ .name = "target",
+ .select_ops = nft_target_select_ops,
++ .release_ops = nft_target_release_ops,
+ .policy = nft_target_policy,
+ .maxattr = NFTA_TARGET_MAX,
+ .owner = THIS_MODULE,
+ };
+
+-static int __net_init nft_compat_init_net(struct net *net)
+-{
+- struct nft_compat_net *cn = nft_compat_pernet(net);
+-
+- INIT_LIST_HEAD(&cn->nft_target_list);
+- INIT_LIST_HEAD(&cn->nft_match_list);
+-
+- return 0;
+-}
+-
+-static void __net_exit nft_compat_exit_net(struct net *net)
+-{
+- struct nft_compat_net *cn = nft_compat_pernet(net);
+- struct nft_xt *xt, *next;
+-
+- if (list_empty(&cn->nft_match_list) &&
+- list_empty(&cn->nft_target_list))
+- return;
+-
+- /* If there was an error that caused nft_xt expr to not be initialized
+- * fully and noone else requested the same expression later, the lists
+- * contain 0-refcount entries that still hold module reference.
+- *
+- * Clean them here.
+- */
+- mutex_lock(&net->nft.commit_mutex);
+- list_for_each_entry_safe(xt, next, &cn->nft_target_list, head) {
+- struct xt_target *target = xt->ops.data;
+-
+- list_del_init(&xt->head);
+-
+- if (refcount_read(&xt->refcnt))
+- continue;
+- module_put(target->me);
+- kfree(xt);
+- }
+-
+- list_for_each_entry_safe(xt, next, &cn->nft_match_list, head) {
+- struct xt_match *match = xt->ops.data;
+-
+- list_del_init(&xt->head);
+-
+- if (refcount_read(&xt->refcnt))
+- continue;
+- module_put(match->me);
+- kfree(xt);
+- }
+- mutex_unlock(&net->nft.commit_mutex);
+-}
+-
+-static struct pernet_operations nft_compat_net_ops = {
+- .init = nft_compat_init_net,
+- .exit = nft_compat_exit_net,
+- .id = &nft_compat_net_id,
+- .size = sizeof(struct nft_compat_net),
+-};
+-
+ static int __init nft_compat_module_init(void)
+ {
+ int ret;
+
+- ret = register_pernet_subsys(&nft_compat_net_ops);
+- if (ret < 0)
+- goto err_target;
+-
+ ret = nft_register_expr(&nft_match_type);
+ if (ret < 0)
+- goto err_pernet;
++ return ret;
+
+ ret = nft_register_expr(&nft_target_type);
+ if (ret < 0)
+@@ -1034,8 +866,6 @@ err_target:
+ nft_unregister_expr(&nft_target_type);
+ err_match:
+ nft_unregister_expr(&nft_match_type);
+-err_pernet:
+- unregister_pernet_subsys(&nft_compat_net_ops);
+ return ret;
+ }
+
+@@ -1044,7 +874,6 @@ static void __exit nft_compat_module_exit(void)
+ nfnetlink_subsys_unregister(&nfnl_compat_subsys);
+ nft_unregister_expr(&nft_target_type);
+ nft_unregister_expr(&nft_match_type);
+- unregister_pernet_subsys(&nft_compat_net_ops);
+ }
+
+ MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFT_COMPAT);
+--
+2.19.1
+
--- /dev/null
+From 2f8519f162e42bbcf99d5637f713a5066cc97f2d Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Mon, 14 Jan 2019 14:28:48 +0100
+Subject: netfilter: nft_compat: use refcnt_t type for nft_xt reference count
+
+[ Upstream commit 12c44aba6618b7f6c437076e5722237190f6cd5f ]
+
+Using standard integer type was fine while all operations on it were
+guarded by the nftnl subsys mutex.
+
+This isn't true anymore:
+1. transactions are guarded only by a pernet mutex, so concurrent
+ rule manipulation in different netns is racy
+2. the ->destroy hook runs from a work queue after the transaction
+ mutex has been released already.
+
+cpu0 cpu1 (net 1) cpu2 (net 2)
+ kworker
+ nft_compat->destroy nft_compat->init nft_compat->init
+ if (--nft_xt->ref == 0) nft_xt->ref++ nft_xt->ref++
+
+Switch to refcount_t. Doing this however only fixes a minor aspect,
+nft_compat also performs linked-list operations in an unsafe way.
+
+This is addressed in the next two patches.
+
+Fixes: f102d66b335a ("netfilter: nf_tables: use dedicated mutex to guard transactions")
+Fixes: 0935d5588400 ("netfilter: nf_tables: asynchronous release")
+Reported-by: Taehee Yoo <ap420073@gmail.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/netfilter/nft_compat.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
+index 38da1f5436b4..24ec9552e126 100644
+--- a/net/netfilter/nft_compat.c
++++ b/net/netfilter/nft_compat.c
+@@ -26,7 +26,7 @@
+ struct nft_xt {
+ struct list_head head;
+ struct nft_expr_ops ops;
+- unsigned int refcnt;
++ refcount_t 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
+@@ -45,7 +45,7 @@ struct nft_xt_match_priv {
+
+ static bool nft_xt_put(struct nft_xt *xt)
+ {
+- if (--xt->refcnt == 0) {
++ if (refcount_dec_and_test(&xt->refcnt)) {
+ list_del(&xt->head);
+ kfree_rcu(xt, rcu_head);
+ return true;
+@@ -273,7 +273,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+ return -EINVAL;
+
+ nft_xt = container_of(expr->ops, struct nft_xt, ops);
+- nft_xt->refcnt++;
++ refcount_inc(&nft_xt->refcnt);
+ return 0;
+ }
+
+@@ -468,7 +468,7 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+ return ret;
+
+ nft_xt = container_of(expr->ops, struct nft_xt, ops);
+- nft_xt->refcnt++;
++ refcount_inc(&nft_xt->refcnt);
+ return 0;
+ }
+
+@@ -770,7 +770,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,
+ goto err;
+ }
+
+- nft_match->refcnt = 0;
++ refcount_set(&nft_match->refcnt, 0);
+ nft_match->ops.type = &nft_match_type;
+ nft_match->ops.eval = nft_match_eval;
+ nft_match->ops.init = nft_match_init;
+@@ -874,7 +874,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
+ goto err;
+ }
+
+- nft_target->refcnt = 0;
++ refcount_set(&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;
+@@ -945,7 +945,7 @@ static void __exit nft_compat_module_exit(void)
+ list_for_each_entry_safe(xt, next, &nft_target_list, head) {
+ struct xt_target *target = xt->ops.data;
+
+- if (WARN_ON_ONCE(xt->refcnt))
++ if (WARN_ON_ONCE(refcount_read(&xt->refcnt)))
+ continue;
+ module_put(target->me);
+ kfree(xt);
+@@ -954,7 +954,7 @@ static void __exit nft_compat_module_exit(void)
+ list_for_each_entry_safe(xt, next, &nft_match_list, head) {
+ struct xt_match *match = xt->ops.data;
+
+- if (WARN_ON_ONCE(xt->refcnt))
++ if (WARN_ON_ONCE(refcount_read(&xt->refcnt)))
+ continue;
+ module_put(match->me);
+ kfree(xt);
+--
+2.19.1
+
--- /dev/null
+From cf4a72f3b266c8ca640ae9d552fbc673c4bbe21f Mon Sep 17 00:00:00 2001
+From: Christophe Leroy <christophe.leroy@c-s.fr>
+Date: Thu, 4 Apr 2019 12:20:05 +0000
+Subject: powerpc/vdso32: fix CLOCK_MONOTONIC on PPC64
+
+[ Upstream commit dd9a994fc68d196a052b73747e3366c57d14a09e ]
+
+Commit b5b4453e7912 ("powerpc/vdso64: Fix CLOCK_MONOTONIC
+inconsistencies across Y2038") changed the type of wtom_clock_sec
+to s64 on PPC64. Therefore, VDSO32 needs to read it with a 4 bytes
+shift in order to retrieve the lower part of it.
+
+Fixes: b5b4453e7912 ("powerpc/vdso64: Fix CLOCK_MONOTONIC inconsistencies across Y2038")
+Reported-by: Christian Zigotzky <chzigotzky@xenosoft.de>
+Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
+Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/powerpc/kernel/vdso32/gettimeofday.S | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/powerpc/kernel/vdso32/gettimeofday.S b/arch/powerpc/kernel/vdso32/gettimeofday.S
+index 769c2624e0a6..75cff3f336b3 100644
+--- a/arch/powerpc/kernel/vdso32/gettimeofday.S
++++ b/arch/powerpc/kernel/vdso32/gettimeofday.S
+@@ -98,7 +98,7 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime)
+ * can be used, r7 contains NSEC_PER_SEC.
+ */
+
+- lwz r5,WTOM_CLOCK_SEC(r9)
++ lwz r5,(WTOM_CLOCK_SEC+LOPART)(r9)
+ lwz r6,WTOM_CLOCK_NSEC(r9)
+
+ /* We now have our offset in r5,r6. We create a fake dependency
+--
+2.19.1
+
--- /dev/null
+netfilter-nft_compat-use-refcnt_t-type-for-nft_xt-re.patch
+netfilter-nft_compat-make-lists-per-netns.patch
+netfilter-nf_tables-split-set-destruction-in-deactiv.patch
+netfilter-nft_compat-destroy-function-must-not-have-.patch
+netfilter-nf_tables-warn-when-expr-implements-only-o.patch
+netfilter-nf_tables-unbind-set-in-rule-from-commit-p.patch
+netfilter-nft_compat-don-t-use-refcount_inc-on-newly.patch
+netfilter-nft_compat-use-.release_ops-and-remove-lis.patch
+netfilter-nf_tables-fix-set-double-free-in-abort-pat.patch
+netfilter-nf_tables-bogus-ebusy-when-deleting-set-af.patch
+netfilter-nf_tables-bogus-ebusy-in-helper-removal-fr.patch
+net-ibmvnic-fix-rtnl-deadlock-during-device-reset.patch
+net-mvpp2-fix-validate-for-ppv2.1.patch
+ext4-fix-some-error-pointer-dereferences.patch
+tipc-handle-the-err-returned-from-cmd-header-functio.patch
+loop-do-not-print-warn-message-if-partition-scan-is-.patch
+drm-rockchip-fix-for-mailbox-read-validation.patch
+vsock-virtio-fix-kernel-panic-from-virtio_transport_.patch
+ipvs-fix-warning-on-unused-variable.patch
+powerpc-vdso32-fix-clock_monotonic-on-ppc64.patch
+alsa-hda-ca0132-fix-build-error-without-config_pci.patch
+net-dsa-mv88e6xxx-add-call-to-mv88e6xxx_ports_cmode_.patch
--- /dev/null
+From ba2d66867538c54f70fc2c939e9bbfdc0b19d507 Mon Sep 17 00:00:00 2001
+From: Xin Long <lucien.xin@gmail.com>
+Date: Sun, 31 Mar 2019 22:50:10 +0800
+Subject: tipc: handle the err returned from cmd header function
+
+[ Upstream commit 2ac695d1d602ce00b12170242f58c3d3a8e36d04 ]
+
+Syzbot found a crash:
+
+ BUG: KMSAN: uninit-value in tipc_nl_compat_name_table_dump+0x54f/0xcd0 net/tipc/netlink_compat.c:872
+ Call Trace:
+ tipc_nl_compat_name_table_dump+0x54f/0xcd0 net/tipc/netlink_compat.c:872
+ __tipc_nl_compat_dumpit+0x59e/0xda0 net/tipc/netlink_compat.c:215
+ tipc_nl_compat_dumpit+0x63a/0x820 net/tipc/netlink_compat.c:280
+ tipc_nl_compat_handle net/tipc/netlink_compat.c:1226 [inline]
+ tipc_nl_compat_recv+0x1b5f/0x2750 net/tipc/netlink_compat.c:1265
+ genl_family_rcv_msg net/netlink/genetlink.c:601 [inline]
+ genl_rcv_msg+0x185f/0x1a60 net/netlink/genetlink.c:626
+ netlink_rcv_skb+0x431/0x620 net/netlink/af_netlink.c:2477
+ genl_rcv+0x63/0x80 net/netlink/genetlink.c:637
+ netlink_unicast_kernel net/netlink/af_netlink.c:1310 [inline]
+ netlink_unicast+0xf3e/0x1020 net/netlink/af_netlink.c:1336
+ netlink_sendmsg+0x127f/0x1300 net/netlink/af_netlink.c:1917
+ sock_sendmsg_nosec net/socket.c:622 [inline]
+ sock_sendmsg net/socket.c:632 [inline]
+
+ Uninit was created at:
+ __alloc_skb+0x309/0xa20 net/core/skbuff.c:208
+ alloc_skb include/linux/skbuff.h:1012 [inline]
+ netlink_alloc_large_skb net/netlink/af_netlink.c:1182 [inline]
+ netlink_sendmsg+0xb82/0x1300 net/netlink/af_netlink.c:1892
+ sock_sendmsg_nosec net/socket.c:622 [inline]
+ sock_sendmsg net/socket.c:632 [inline]
+
+It was supposed to be fixed on commit 974cb0e3e7c9 ("tipc: fix uninit-value
+in tipc_nl_compat_name_table_dump") by checking TLV_GET_DATA_LEN(msg->req)
+in cmd->header()/tipc_nl_compat_name_table_dump_header(), which is called
+ahead of tipc_nl_compat_name_table_dump().
+
+However, tipc_nl_compat_dumpit() doesn't handle the error returned from cmd
+header function. It means even when the check added in that fix fails, it
+won't stop calling tipc_nl_compat_name_table_dump(), and the issue will be
+triggered again.
+
+So this patch is to add the process for the err returned from cmd header
+function in tipc_nl_compat_dumpit().
+
+Reported-by: syzbot+3ce8520484b0d4e260a5@syzkaller.appspotmail.com
+Signed-off-by: Xin Long <lucien.xin@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/tipc/netlink_compat.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c
+index 0b21187d74df..e3de41eb0000 100644
+--- a/net/tipc/netlink_compat.c
++++ b/net/tipc/netlink_compat.c
+@@ -267,8 +267,14 @@ static int tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
+ if (msg->rep_type)
+ tipc_tlv_init(msg->rep, msg->rep_type);
+
+- if (cmd->header)
+- (*cmd->header)(msg);
++ if (cmd->header) {
++ err = (*cmd->header)(msg);
++ if (err) {
++ kfree_skb(msg->rep);
++ msg->rep = NULL;
++ return err;
++ }
++ }
+
+ arg = nlmsg_new(0, GFP_KERNEL);
+ if (!arg) {
+--
+2.19.1
+
--- /dev/null
+From b787d341d1cac5954b5f86c22ca6db51c419d127 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Adalbert=20Laz=C4=83r?= <alazar@bitdefender.com>
+Date: Wed, 6 Mar 2019 12:13:53 +0200
+Subject: vsock/virtio: fix kernel panic from virtio_transport_reset_no_sock
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+[ Upstream commit 4c404ce23358d5d8fbdeb7a6021a9b33d3c3c167 ]
+
+Previous to commit 22b5c0b63f32 ("vsock/virtio: fix kernel panic
+after device hot-unplug"), vsock_core_init() was called from
+virtio_vsock_probe(). Now, virtio_transport_reset_no_sock() can be called
+before vsock_core_init() has the chance to run.
+
+[Wed Feb 27 14:17:09 2019] BUG: unable to handle kernel NULL pointer dereference at 0000000000000110
+[Wed Feb 27 14:17:09 2019] #PF error: [normal kernel read fault]
+[Wed Feb 27 14:17:09 2019] PGD 0 P4D 0
+[Wed Feb 27 14:17:09 2019] Oops: 0000 [#1] SMP PTI
+[Wed Feb 27 14:17:09 2019] CPU: 3 PID: 59 Comm: kworker/3:1 Not tainted 5.0.0-rc7-390-generic-hvi #390
+[Wed Feb 27 14:17:09 2019] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
+[Wed Feb 27 14:17:09 2019] Workqueue: virtio_vsock virtio_transport_rx_work [vmw_vsock_virtio_transport]
+[Wed Feb 27 14:17:09 2019] RIP: 0010:virtio_transport_reset_no_sock+0x8c/0xc0 [vmw_vsock_virtio_transport_common]
+[Wed Feb 27 14:17:09 2019] Code: 35 8b 4f 14 48 8b 57 08 31 f6 44 8b 4f 10 44 8b 07 48 8d 7d c8 e8 84 f8 ff ff 48 85 c0 48 89 c3 74 2a e8 f7 31 03 00 48 89 df <48> 8b 80 10 01 00 00 e8 68 fb 69 ed 48 8b 75 f0 65 48 33 34 25 28
+[Wed Feb 27 14:17:09 2019] RSP: 0018:ffffb42701ab7d40 EFLAGS: 00010282
+[Wed Feb 27 14:17:09 2019] RAX: 0000000000000000 RBX: ffff9d79637ee080 RCX: 0000000000000003
+[Wed Feb 27 14:17:09 2019] RDX: 0000000000000001 RSI: 0000000000000002 RDI: ffff9d79637ee080
+[Wed Feb 27 14:17:09 2019] RBP: ffffb42701ab7d78 R08: ffff9d796fae70e0 R09: ffff9d796f403500
+[Wed Feb 27 14:17:09 2019] R10: ffffb42701ab7d90 R11: 0000000000000000 R12: ffff9d7969d09240
+[Wed Feb 27 14:17:09 2019] R13: ffff9d79624e6840 R14: ffff9d7969d09318 R15: ffff9d796d48ff80
+[Wed Feb 27 14:17:09 2019] FS: 0000000000000000(0000) GS:ffff9d796fac0000(0000) knlGS:0000000000000000
+[Wed Feb 27 14:17:09 2019] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+[Wed Feb 27 14:17:09 2019] CR2: 0000000000000110 CR3: 0000000427f22000 CR4: 00000000000006e0
+[Wed Feb 27 14:17:09 2019] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
+[Wed Feb 27 14:17:09 2019] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
+[Wed Feb 27 14:17:09 2019] Call Trace:
+[Wed Feb 27 14:17:09 2019] virtio_transport_recv_pkt+0x63/0x820 [vmw_vsock_virtio_transport_common]
+[Wed Feb 27 14:17:09 2019] ? kfree+0x17e/0x190
+[Wed Feb 27 14:17:09 2019] ? detach_buf_split+0x145/0x160
+[Wed Feb 27 14:17:09 2019] ? __switch_to_asm+0x40/0x70
+[Wed Feb 27 14:17:09 2019] virtio_transport_rx_work+0xa0/0x106 [vmw_vsock_virtio_transport]
+[Wed Feb 27 14:17:09 2019] NET: Registered protocol family 40
+[Wed Feb 27 14:17:09 2019] process_one_work+0x167/0x410
+[Wed Feb 27 14:17:09 2019] worker_thread+0x4d/0x460
+[Wed Feb 27 14:17:09 2019] kthread+0x105/0x140
+[Wed Feb 27 14:17:09 2019] ? rescuer_thread+0x360/0x360
+[Wed Feb 27 14:17:09 2019] ? kthread_destroy_worker+0x50/0x50
+[Wed Feb 27 14:17:09 2019] ret_from_fork+0x35/0x40
+[Wed Feb 27 14:17:09 2019] Modules linked in: vmw_vsock_virtio_transport vmw_vsock_virtio_transport_common input_leds vsock serio_raw i2c_piix4 mac_hid qemu_fw_cfg autofs4 cirrus ttm drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops virtio_net psmouse drm net_failover pata_acpi virtio_blk failover floppy
+
+Fixes: 22b5c0b63f32 ("vsock/virtio: fix kernel panic after device hot-unplug")
+Reported-by: Alexandru Herghelegiu <aherghelegiu@bitdefender.com>
+Signed-off-by: Adalbert Lazăr <alazar@bitdefender.com>
+Co-developed-by: Stefan Hajnoczi <stefanha@redhat.com>
+Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
+Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/vmw_vsock/virtio_transport_common.c | 22 +++++++++++++++-------
+ 1 file changed, 15 insertions(+), 7 deletions(-)
+
+diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
+index 3ae3a33da70b..602715fc9a75 100644
+--- a/net/vmw_vsock/virtio_transport_common.c
++++ b/net/vmw_vsock/virtio_transport_common.c
+@@ -662,6 +662,8 @@ static int virtio_transport_reset(struct vsock_sock *vsk,
+ */
+ static int virtio_transport_reset_no_sock(struct virtio_vsock_pkt *pkt)
+ {
++ const struct virtio_transport *t;
++ struct virtio_vsock_pkt *reply;
+ struct virtio_vsock_pkt_info info = {
+ .op = VIRTIO_VSOCK_OP_RST,
+ .type = le16_to_cpu(pkt->hdr.type),
+@@ -672,15 +674,21 @@ static int virtio_transport_reset_no_sock(struct virtio_vsock_pkt *pkt)
+ if (le16_to_cpu(pkt->hdr.op) == VIRTIO_VSOCK_OP_RST)
+ return 0;
+
+- pkt = virtio_transport_alloc_pkt(&info, 0,
+- le64_to_cpu(pkt->hdr.dst_cid),
+- le32_to_cpu(pkt->hdr.dst_port),
+- le64_to_cpu(pkt->hdr.src_cid),
+- le32_to_cpu(pkt->hdr.src_port));
+- if (!pkt)
++ reply = virtio_transport_alloc_pkt(&info, 0,
++ le64_to_cpu(pkt->hdr.dst_cid),
++ le32_to_cpu(pkt->hdr.dst_port),
++ le64_to_cpu(pkt->hdr.src_cid),
++ le32_to_cpu(pkt->hdr.src_port));
++ if (!reply)
+ return -ENOMEM;
+
+- return virtio_transport_get_ops()->send_pkt(pkt);
++ t = virtio_transport_get_ops();
++ if (!t) {
++ virtio_transport_free_pkt(reply);
++ return -ENOTCONN;
++ }
++
++ return t->send_pkt(reply);
+ }
+
+ static void virtio_transport_wait_close(struct sock *sk, long timeout)
+--
+2.19.1
+