From: Sasha Levin Date: Sat, 27 Apr 2019 15:37:26 +0000 (-0400) Subject: patches for 4.19 X-Git-Tag: v4.9.172~54 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=030f340095aa890c837cafb00e012933c28247bf;p=thirdparty%2Fkernel%2Fstable-queue.git patches for 4.19 Signed-off-by: Sasha Levin --- 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 index 00000000000..07b8298f92a --- /dev/null +++ b/queue-4.19/alsa-hda-ca0132-fix-build-error-without-config_pci.patch @@ -0,0 +1,40 @@ +From 0ecd74260f37da7bcb66823aca68457f00ac061d Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +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 +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..1753f0ccb70 --- /dev/null +++ b/queue-4.19/drm-rockchip-fix-for-mailbox-read-validation.patch @@ -0,0 +1,37 @@ +From 906e35d2764c3df0836120e2aed8b9e4abcbcc3b Mon Sep 17 00:00:00 2001 +From: Damian Kos +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 +Signed-off-by: Heiko Stuebner +Link: https://patchwork.freedesktop.org/patch/msgid/1542640463-18332-1-git-send-email-dkos@cadence.com +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..5a1aa813a02 --- /dev/null +++ b/queue-4.19/ext4-fix-some-error-pointer-dereferences.patch @@ -0,0 +1,49 @@ +From d7c94b111be50a85c94c89462a7d091e64ca448d Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +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 +Signed-off-by: Theodore Ts'o +Reviewed-by: Jan Kara +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..c9aaf91bcac --- /dev/null +++ b/queue-4.19/ipvs-fix-warning-on-unused-variable.patch @@ -0,0 +1,53 @@ +From 3b94e7e7789719a3bcf30dc6ac09d42ca187975d Mon Sep 17 00:00:00 2001 +From: Andrea Claudi +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 +Signed-off-by: Andrea Claudi +Reviewed-by: Stefano Brivio +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..ef30915bb85 --- /dev/null +++ b/queue-4.19/loop-do-not-print-warn-message-if-partition-scan-is-.patch @@ -0,0 +1,37 @@ +From ff9f726364fdb4ef0e4335d9e822dc8ed9edff24 Mon Sep 17 00:00:00 2001 +From: Dongli Zhang +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 +Reviewed-by: Jan Kara +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..bd67f028292 --- /dev/null +++ b/queue-4.19/net-dsa-mv88e6xxx-add-call-to-mv88e6xxx_ports_cmode_.patch @@ -0,0 +1,37 @@ +From cba0ee0aae036508a290a2a24ed8a9eeac9ba4d2 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +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 +Suggested-by: Andrew Lunn +Signed-off-by: Heiner Kallweit +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..70cec5c7a86 --- /dev/null +++ b/queue-4.19/net-ibmvnic-fix-rtnl-deadlock-during-device-reset.patch @@ -0,0 +1,38 @@ +From 6ab72aac366e00516bc711659720ae8bf2e51db7 Mon Sep 17 00:00:00 2001 +From: Thomas Falcon +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 +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..5ce2d95a5d0 --- /dev/null +++ b/queue-4.19/net-mvpp2-fix-validate-for-ppv2.1.patch @@ -0,0 +1,37 @@ +From f642c21f2f0efea07d99b8c641809bd9c1599608 Mon Sep 17 00:00:00 2001 +From: Antoine Tenart +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 +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..634d8af9037 --- /dev/null +++ b/queue-4.19/netfilter-nf_tables-bogus-ebusy-in-helper-removal-fr.patch @@ -0,0 +1,63 @@ +From 3efac4b5c249a680c5fa3ca4e1ba8c0c2f70973c Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..903a36a7322 --- /dev/null +++ b/queue-4.19/netfilter-nf_tables-bogus-ebusy-when-deleting-set-af.patch @@ -0,0 +1,223 @@ +From 34d7ff470b8db0226aaf121b5ef37dd2071b2d1a Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..3f5df897008 --- /dev/null +++ b/queue-4.19/netfilter-nf_tables-fix-set-double-free-in-abort-pat.patch @@ -0,0 +1,134 @@ +From 065a6f5981ae7b110a1825f65712096e2897e29b Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..edd536c8183 --- /dev/null +++ b/queue-4.19/netfilter-nf_tables-split-set-destruction-in-deactiv.patch @@ -0,0 +1,268 @@ +From f5faae44f51f956af30ef1495f50058f616516a8 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..6a2b75b3312 --- /dev/null +++ b/queue-4.19/netfilter-nf_tables-unbind-set-in-rule-from-commit-p.patch @@ -0,0 +1,447 @@ +From 81bfceedb55c7e59144b1a1b885f0bbf2ff17dda Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +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 +Signed-off-by: Pablo Neira Ayuso +--- + 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 index 00000000000..28b24da8a81 --- /dev/null +++ b/queue-4.19/netfilter-nf_tables-warn-when-expr-implements-only-o.patch @@ -0,0 +1,72 @@ +From 760cc71ec9c5a078e633cebbb40f6430771b4532 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +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 +Signed-off-by: Pablo Neira Ayuso +--- + 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 index 00000000000..fe75cb6ce29 --- /dev/null +++ b/queue-4.19/netfilter-nft_compat-destroy-function-must-not-have-.patch @@ -0,0 +1,135 @@ +From 05bfe7741101dbec96b27d75de4e22b1b367fe6c Mon Sep 17 00:00:00 2001 +From: Florian Westphal +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 +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +--- + 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 index 00000000000..6d5968e4c21 --- /dev/null +++ b/queue-4.19/netfilter-nft_compat-don-t-use-refcount_inc-on-newly.patch @@ -0,0 +1,164 @@ +From 3e6101cb326f2b53356dbc4c4fee3dc7c805399c Mon Sep 17 00:00:00 2001 +From: Florian Westphal +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 +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..606a27bf1fe --- /dev/null +++ b/queue-4.19/netfilter-nft_compat-make-lists-per-netns.patch @@ -0,0 +1,293 @@ +From 26dcae77515ded6380a396fcd6834cd41cdb1764 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +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 +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + 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 + #include + #include ++#include + + 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 index 00000000000..a7c792d2968 --- /dev/null +++ b/queue-4.19/netfilter-nft_compat-use-.release_ops-and-remove-lis.patch @@ -0,0 +1,537 @@ +From 42df9defd5f071d978dd3db8c21adf2ff7b2b46d Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +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 +Signed-off-by: Sasha Levin +--- + 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 + #include + #include +-#include +- +-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 index 00000000000..9f80b799cef --- /dev/null +++ b/queue-4.19/netfilter-nft_compat-use-refcnt_t-type-for-nft_xt-re.patch @@ -0,0 +1,115 @@ +From 2f8519f162e42bbcf99d5637f713a5066cc97f2d Mon Sep 17 00:00:00 2001 +From: Florian Westphal +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 +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..094be71497e --- /dev/null +++ b/queue-4.19/powerpc-vdso32-fix-clock_monotonic-on-ppc64.patch @@ -0,0 +1,37 @@ +From cf4a72f3b266c8ca640ae9d552fbc673c4bbe21f Mon Sep 17 00:00:00 2001 +From: Christophe Leroy +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 +Signed-off-by: Christophe Leroy +Signed-off-by: Michael Ellerman +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..c216b791e15 --- /dev/null +++ b/queue-4.19/series @@ -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 index 00000000000..fbc4cc493e5 --- /dev/null +++ b/queue-4.19/tipc-handle-the-err-returned-from-cmd-header-functio.patch @@ -0,0 +1,79 @@ +From ba2d66867538c54f70fc2c939e9bbfdc0b19d507 Mon Sep 17 00:00:00 2001 +From: Xin Long +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 +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..f3f116d7709 --- /dev/null +++ b/queue-4.19/vsock-virtio-fix-kernel-panic-from-virtio_transport_.patch @@ -0,0 +1,107 @@ +From b787d341d1cac5954b5f86c22ca6db51c419d127 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Adalbert=20Laz=C4=83r?= +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 +Signed-off-by: Adalbert Lazăr +Co-developed-by: Stefan Hajnoczi +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Stefano Garzarella +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + 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 +