]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
patches for 4.19
authorSasha Levin <sashal@kernel.org>
Sat, 27 Apr 2019 15:37:26 +0000 (11:37 -0400)
committerSasha Levin <sashal@kernel.org>
Sat, 27 Apr 2019 15:37:26 +0000 (11:37 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
23 files changed:
queue-4.19/alsa-hda-ca0132-fix-build-error-without-config_pci.patch [new file with mode: 0644]
queue-4.19/drm-rockchip-fix-for-mailbox-read-validation.patch [new file with mode: 0644]
queue-4.19/ext4-fix-some-error-pointer-dereferences.patch [new file with mode: 0644]
queue-4.19/ipvs-fix-warning-on-unused-variable.patch [new file with mode: 0644]
queue-4.19/loop-do-not-print-warn-message-if-partition-scan-is-.patch [new file with mode: 0644]
queue-4.19/net-dsa-mv88e6xxx-add-call-to-mv88e6xxx_ports_cmode_.patch [new file with mode: 0644]
queue-4.19/net-ibmvnic-fix-rtnl-deadlock-during-device-reset.patch [new file with mode: 0644]
queue-4.19/net-mvpp2-fix-validate-for-ppv2.1.patch [new file with mode: 0644]
queue-4.19/netfilter-nf_tables-bogus-ebusy-in-helper-removal-fr.patch [new file with mode: 0644]
queue-4.19/netfilter-nf_tables-bogus-ebusy-when-deleting-set-af.patch [new file with mode: 0644]
queue-4.19/netfilter-nf_tables-fix-set-double-free-in-abort-pat.patch [new file with mode: 0644]
queue-4.19/netfilter-nf_tables-split-set-destruction-in-deactiv.patch [new file with mode: 0644]
queue-4.19/netfilter-nf_tables-unbind-set-in-rule-from-commit-p.patch [new file with mode: 0644]
queue-4.19/netfilter-nf_tables-warn-when-expr-implements-only-o.patch [new file with mode: 0644]
queue-4.19/netfilter-nft_compat-destroy-function-must-not-have-.patch [new file with mode: 0644]
queue-4.19/netfilter-nft_compat-don-t-use-refcount_inc-on-newly.patch [new file with mode: 0644]
queue-4.19/netfilter-nft_compat-make-lists-per-netns.patch [new file with mode: 0644]
queue-4.19/netfilter-nft_compat-use-.release_ops-and-remove-lis.patch [new file with mode: 0644]
queue-4.19/netfilter-nft_compat-use-refcnt_t-type-for-nft_xt-re.patch [new file with mode: 0644]
queue-4.19/powerpc-vdso32-fix-clock_monotonic-on-ppc64.patch [new file with mode: 0644]
queue-4.19/series [new file with mode: 0644]
queue-4.19/tipc-handle-the-err-returned-from-cmd-header-functio.patch [new file with mode: 0644]
queue-4.19/vsock-virtio-fix-kernel-panic-from-virtio_transport_.patch [new file with mode: 0644]

diff --git a/queue-4.19/alsa-hda-ca0132-fix-build-error-without-config_pci.patch b/queue-4.19/alsa-hda-ca0132-fix-build-error-without-config_pci.patch
new file mode 100644 (file)
index 0000000..07b8298
--- /dev/null
@@ -0,0 +1,40 @@
+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
+
diff --git a/queue-4.19/drm-rockchip-fix-for-mailbox-read-validation.patch b/queue-4.19/drm-rockchip-fix-for-mailbox-read-validation.patch
new file mode 100644 (file)
index 0000000..1753f0c
--- /dev/null
@@ -0,0 +1,37 @@
+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
+
diff --git a/queue-4.19/ext4-fix-some-error-pointer-dereferences.patch b/queue-4.19/ext4-fix-some-error-pointer-dereferences.patch
new file mode 100644 (file)
index 0000000..5a1aa81
--- /dev/null
@@ -0,0 +1,49 @@
+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
+
diff --git a/queue-4.19/ipvs-fix-warning-on-unused-variable.patch b/queue-4.19/ipvs-fix-warning-on-unused-variable.patch
new file mode 100644 (file)
index 0000000..c9aaf91
--- /dev/null
@@ -0,0 +1,53 @@
+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
+
diff --git a/queue-4.19/loop-do-not-print-warn-message-if-partition-scan-is-.patch b/queue-4.19/loop-do-not-print-warn-message-if-partition-scan-is-.patch
new file mode 100644 (file)
index 0000000..ef30915
--- /dev/null
@@ -0,0 +1,37 @@
+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
+
diff --git a/queue-4.19/net-dsa-mv88e6xxx-add-call-to-mv88e6xxx_ports_cmode_.patch b/queue-4.19/net-dsa-mv88e6xxx-add-call-to-mv88e6xxx_ports_cmode_.patch
new file mode 100644 (file)
index 0000000..bd67f02
--- /dev/null
@@ -0,0 +1,37 @@
+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
+
diff --git a/queue-4.19/net-ibmvnic-fix-rtnl-deadlock-during-device-reset.patch b/queue-4.19/net-ibmvnic-fix-rtnl-deadlock-during-device-reset.patch
new file mode 100644 (file)
index 0000000..70cec5c
--- /dev/null
@@ -0,0 +1,38 @@
+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
+
diff --git a/queue-4.19/net-mvpp2-fix-validate-for-ppv2.1.patch b/queue-4.19/net-mvpp2-fix-validate-for-ppv2.1.patch
new file mode 100644 (file)
index 0000000..5ce2d95
--- /dev/null
@@ -0,0 +1,37 @@
+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
+
diff --git a/queue-4.19/netfilter-nf_tables-bogus-ebusy-in-helper-removal-fr.patch b/queue-4.19/netfilter-nf_tables-bogus-ebusy-in-helper-removal-fr.patch
new file mode 100644 (file)
index 0000000..634d8af
--- /dev/null
@@ -0,0 +1,63 @@
+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
+
diff --git a/queue-4.19/netfilter-nf_tables-bogus-ebusy-when-deleting-set-af.patch b/queue-4.19/netfilter-nf_tables-bogus-ebusy-when-deleting-set-af.patch
new file mode 100644 (file)
index 0000000..903a36a
--- /dev/null
@@ -0,0 +1,223 @@
+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
+
diff --git a/queue-4.19/netfilter-nf_tables-fix-set-double-free-in-abort-pat.patch b/queue-4.19/netfilter-nf_tables-fix-set-double-free-in-abort-pat.patch
new file mode 100644 (file)
index 0000000..3f5df89
--- /dev/null
@@ -0,0 +1,134 @@
+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
+
diff --git a/queue-4.19/netfilter-nf_tables-split-set-destruction-in-deactiv.patch b/queue-4.19/netfilter-nf_tables-split-set-destruction-in-deactiv.patch
new file mode 100644 (file)
index 0000000..edd536c
--- /dev/null
@@ -0,0 +1,268 @@
+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
+
diff --git a/queue-4.19/netfilter-nf_tables-unbind-set-in-rule-from-commit-p.patch b/queue-4.19/netfilter-nf_tables-unbind-set-in-rule-from-commit-p.patch
new file mode 100644 (file)
index 0000000..6a2b75b
--- /dev/null
@@ -0,0 +1,447 @@
+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
+
diff --git a/queue-4.19/netfilter-nf_tables-warn-when-expr-implements-only-o.patch b/queue-4.19/netfilter-nf_tables-warn-when-expr-implements-only-o.patch
new file mode 100644 (file)
index 0000000..28b24da
--- /dev/null
@@ -0,0 +1,72 @@
+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
+
diff --git a/queue-4.19/netfilter-nft_compat-destroy-function-must-not-have-.patch b/queue-4.19/netfilter-nft_compat-destroy-function-must-not-have-.patch
new file mode 100644 (file)
index 0000000..fe75cb6
--- /dev/null
@@ -0,0 +1,135 @@
+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
+
diff --git a/queue-4.19/netfilter-nft_compat-don-t-use-refcount_inc-on-newly.patch b/queue-4.19/netfilter-nft_compat-don-t-use-refcount_inc-on-newly.patch
new file mode 100644 (file)
index 0000000..6d5968e
--- /dev/null
@@ -0,0 +1,164 @@
+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
+
diff --git a/queue-4.19/netfilter-nft_compat-make-lists-per-netns.patch b/queue-4.19/netfilter-nft_compat-make-lists-per-netns.patch
new file mode 100644 (file)
index 0000000..606a27b
--- /dev/null
@@ -0,0 +1,293 @@
+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
+
diff --git a/queue-4.19/netfilter-nft_compat-use-.release_ops-and-remove-lis.patch b/queue-4.19/netfilter-nft_compat-use-.release_ops-and-remove-lis.patch
new file mode 100644 (file)
index 0000000..a7c792d
--- /dev/null
@@ -0,0 +1,537 @@
+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
+
diff --git a/queue-4.19/netfilter-nft_compat-use-refcnt_t-type-for-nft_xt-re.patch b/queue-4.19/netfilter-nft_compat-use-refcnt_t-type-for-nft_xt-re.patch
new file mode 100644 (file)
index 0000000..9f80b79
--- /dev/null
@@ -0,0 +1,115 @@
+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
+
diff --git a/queue-4.19/powerpc-vdso32-fix-clock_monotonic-on-ppc64.patch b/queue-4.19/powerpc-vdso32-fix-clock_monotonic-on-ppc64.patch
new file mode 100644 (file)
index 0000000..094be71
--- /dev/null
@@ -0,0 +1,37 @@
+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
+
diff --git a/queue-4.19/series b/queue-4.19/series
new file mode 100644 (file)
index 0000000..c216b79
--- /dev/null
@@ -0,0 +1,22 @@
+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
diff --git a/queue-4.19/tipc-handle-the-err-returned-from-cmd-header-functio.patch b/queue-4.19/tipc-handle-the-err-returned-from-cmd-header-functio.patch
new file mode 100644 (file)
index 0000000..fbc4cc4
--- /dev/null
@@ -0,0 +1,79 @@
+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
+
diff --git a/queue-4.19/vsock-virtio-fix-kernel-panic-from-virtio_transport_.patch b/queue-4.19/vsock-virtio-fix-kernel-panic-from-virtio_transport_.patch
new file mode 100644 (file)
index 0000000..f3f116d
--- /dev/null
@@ -0,0 +1,107 @@
+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
+