From: Greg Kroah-Hartman Date: Mon, 4 Oct 2021 10:51:05 +0000 (+0200) Subject: 5.4-stable patches X-Git-Tag: v4.4.286~30 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9358a0ca4dda3cec2d128db586edd59fd3a94cca;p=thirdparty%2Fkernel%2Fstable-queue.git 5.4-stable patches added patches: libnvdimm-pmem-fix-crash-triggered-when-i-o-in-flight-during-unbind.patch pci-fix-pci_host_bridge-struct-device-release-free-handling.patch --- diff --git a/queue-5.4/libnvdimm-pmem-fix-crash-triggered-when-i-o-in-flight-during-unbind.patch b/queue-5.4/libnvdimm-pmem-fix-crash-triggered-when-i-o-in-flight-during-unbind.patch new file mode 100644 index 00000000000..c0b1b4f187d --- /dev/null +++ b/queue-5.4/libnvdimm-pmem-fix-crash-triggered-when-i-o-in-flight-during-unbind.patch @@ -0,0 +1,82 @@ +From 32b2397c1e56f33b0b1881def965bb89bd12f448 Mon Sep 17 00:00:00 2001 +From: sumiyawang +Date: Sun, 22 Aug 2021 19:49:09 +0800 +Subject: libnvdimm/pmem: Fix crash triggered when I/O in-flight during unbind + +From: sumiyawang + +commit 32b2397c1e56f33b0b1881def965bb89bd12f448 upstream. + +There is a use after free crash when the pmem driver tears down its +mapping while I/O is still inbound. + +This is triggered by driver unbind, "ndctl destroy-namespace", while I/O +is in flight. + +Fix the sequence of blk_cleanup_queue() vs memunmap(). + +The crash signature is of the form: + + BUG: unable to handle page fault for address: ffffc90080200000 + CPU: 36 PID: 9606 Comm: systemd-udevd + Call Trace: + ? pmem_do_bvec+0xf9/0x3a0 + ? xas_alloc+0x55/0xd0 + pmem_rw_page+0x4b/0x80 + bdev_read_page+0x86/0xb0 + do_mpage_readpage+0x5d4/0x7a0 + ? lru_cache_add+0xe/0x10 + mpage_readpages+0xf9/0x1c0 + ? bd_link_disk_holder+0x1a0/0x1a0 + blkdev_readpages+0x1d/0x20 + read_pages+0x67/0x1a0 + + ndctl Call Trace in vmcore: + PID: 23473 TASK: ffff88c4fbbe8000 CPU: 1 COMMAND: "ndctl" + __schedule + schedule + blk_mq_freeze_queue_wait + blk_freeze_queue + blk_cleanup_queue + pmem_release_queue + devm_action_release + release_nodes + devres_release_all + device_release_driver_internal + device_driver_detach + unbind_store + +Cc: +Signed-off-by: sumiyawang +Reviewed-by: yongduan +Link: https://lore.kernel.org/r/1629632949-14749-1-git-send-email-sumiyawang@tencent.com +Fixes: 50f44ee7248a ("mm/devm_memremap_pages: fix final page put race") +Signed-off-by: Dan Williams +[tyhicks: Minor contextual change in pmem_attach_disk() due to the + transition to 'struct range' not yet taking place. Preserve the + memcpy() call rather than initializing the range struct. That change + was introduced in v5.10 with commit a4574f63edc6 ("mm/memremap_pages: + convert to 'struct range'")] +Signed-off-by: Tyler Hicks +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/nvdimm/pmem.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/nvdimm/pmem.c ++++ b/drivers/nvdimm/pmem.c +@@ -423,11 +423,11 @@ static int pmem_attach_disk(struct devic + pmem->pfn_flags |= PFN_MAP; + memcpy(&bb_res, &pmem->pgmap.res, sizeof(bb_res)); + } else { ++ addr = devm_memremap(dev, pmem->phys_addr, ++ pmem->size, ARCH_MEMREMAP_PMEM); + if (devm_add_action_or_reset(dev, pmem_release_queue, + &pmem->pgmap)) + return -ENOMEM; +- addr = devm_memremap(dev, pmem->phys_addr, +- pmem->size, ARCH_MEMREMAP_PMEM); + memcpy(&bb_res, &nsio->res, sizeof(bb_res)); + } + diff --git a/queue-5.4/pci-fix-pci_host_bridge-struct-device-release-free-handling.patch b/queue-5.4/pci-fix-pci_host_bridge-struct-device-release-free-handling.patch new file mode 100644 index 00000000000..2a29271223d --- /dev/null +++ b/queue-5.4/pci-fix-pci_host_bridge-struct-device-release-free-handling.patch @@ -0,0 +1,161 @@ +From 9885440b16b8fc1dd7275800fd28f56a92f60896 Mon Sep 17 00:00:00 2001 +From: Rob Herring +Date: Wed, 13 May 2020 17:38:59 -0500 +Subject: PCI: Fix pci_host_bridge struct device release/free handling + +From: Rob Herring + +commit 9885440b16b8fc1dd7275800fd28f56a92f60896 upstream. + +The PCI code has several paths where the struct pci_host_bridge is freed +directly. This is wrong because it contains a struct device which is +refcounted and should be freed using put_device(). This can result in +use-after-free errors. I think this problem has existed since 2012 with +commit 7b5436635800 ("PCI: add generic device into pci_host_bridge +struct"). It generally hasn't mattered as most host bridge drivers are +still built-in and can't unbind. + +The problem is a struct device should never be freed directly once +device_initialize() is called and a ref is held, but that doesn't happen +until pci_register_host_bridge(). There's then a window between allocating +the host bridge and pci_register_host_bridge() where kfree should be used. +This is fragile and requires callers to do the right thing. To fix this, we +need to split device_register() into device_initialize() and device_add() +calls, so that the host bridge struct is always freed by using a +put_device(). + +devm_pci_alloc_host_bridge() is using devm_kzalloc() to allocate struct +pci_host_bridge which will be freed directly. Instead, we can use a custom +devres action to call put_device(). + +Link: https://lore.kernel.org/r/20200513223859.11295-2-robh@kernel.org +Reported-by: Anders Roxell +Tested-by: Anders Roxell +Signed-off-by: Rob Herring +Signed-off-by: Bjorn Helgaas +Reviewed-by: Lorenzo Pieralisi +Acked-by: Arnd Bergmann +[tyhicks: Minor contextual change in pci_init_host_bridge() due to the + lack of a native_dpc member in the pci_host_bridge struct. It was added + in v5.7 with commit ac1c8e35a326 ("PCI/DPC: Add Error Disconnect + Recover (EDR) support")] +Signed-off-by: Tyler Hicks +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/pci/probe.c | 36 +++++++++++++++++++----------------- + drivers/pci/remove.c | 2 +- + 2 files changed, 20 insertions(+), 18 deletions(-) + +--- a/drivers/pci/probe.c ++++ b/drivers/pci/probe.c +@@ -564,7 +564,7 @@ static struct pci_bus *pci_alloc_bus(str + return b; + } + +-static void devm_pci_release_host_bridge_dev(struct device *dev) ++static void pci_release_host_bridge_dev(struct device *dev) + { + struct pci_host_bridge *bridge = to_pci_host_bridge(dev); + +@@ -573,12 +573,7 @@ static void devm_pci_release_host_bridge + + pci_free_resource_list(&bridge->windows); + pci_free_resource_list(&bridge->dma_ranges); +-} +- +-static void pci_release_host_bridge_dev(struct device *dev) +-{ +- devm_pci_release_host_bridge_dev(dev); +- kfree(to_pci_host_bridge(dev)); ++ kfree(bridge); + } + + static void pci_init_host_bridge(struct pci_host_bridge *bridge) +@@ -597,6 +592,8 @@ static void pci_init_host_bridge(struct + bridge->native_shpc_hotplug = 1; + bridge->native_pme = 1; + bridge->native_ltr = 1; ++ ++ device_initialize(&bridge->dev); + } + + struct pci_host_bridge *pci_alloc_host_bridge(size_t priv) +@@ -614,17 +611,25 @@ struct pci_host_bridge *pci_alloc_host_b + } + EXPORT_SYMBOL(pci_alloc_host_bridge); + ++static void devm_pci_alloc_host_bridge_release(void *data) ++{ ++ pci_free_host_bridge(data); ++} ++ + struct pci_host_bridge *devm_pci_alloc_host_bridge(struct device *dev, + size_t priv) + { ++ int ret; + struct pci_host_bridge *bridge; + +- bridge = devm_kzalloc(dev, sizeof(*bridge) + priv, GFP_KERNEL); ++ bridge = pci_alloc_host_bridge(priv); + if (!bridge) + return NULL; + +- pci_init_host_bridge(bridge); +- bridge->dev.release = devm_pci_release_host_bridge_dev; ++ ret = devm_add_action_or_reset(dev, devm_pci_alloc_host_bridge_release, ++ bridge); ++ if (ret) ++ return NULL; + + return bridge; + } +@@ -632,10 +637,7 @@ EXPORT_SYMBOL(devm_pci_alloc_host_bridge + + void pci_free_host_bridge(struct pci_host_bridge *bridge) + { +- pci_free_resource_list(&bridge->windows); +- pci_free_resource_list(&bridge->dma_ranges); +- +- kfree(bridge); ++ put_device(&bridge->dev); + } + EXPORT_SYMBOL(pci_free_host_bridge); + +@@ -866,7 +868,7 @@ static int pci_register_host_bridge(stru + if (err) + goto free; + +- err = device_register(&bridge->dev); ++ err = device_add(&bridge->dev); + if (err) { + put_device(&bridge->dev); + goto free; +@@ -933,7 +935,7 @@ static int pci_register_host_bridge(stru + + unregister: + put_device(&bridge->dev); +- device_unregister(&bridge->dev); ++ device_del(&bridge->dev); + + free: + kfree(bus); +@@ -2945,7 +2947,7 @@ struct pci_bus *pci_create_root_bus(stru + return bridge->bus; + + err_out: +- kfree(bridge); ++ put_device(&bridge->dev); + return NULL; + } + EXPORT_SYMBOL_GPL(pci_create_root_bus); +--- a/drivers/pci/remove.c ++++ b/drivers/pci/remove.c +@@ -160,6 +160,6 @@ void pci_remove_root_bus(struct pci_bus + host_bridge->bus = NULL; + + /* remove the host bridge */ +- device_unregister(&host_bridge->dev); ++ device_del(&host_bridge->dev); + } + EXPORT_SYMBOL_GPL(pci_remove_root_bus); diff --git a/queue-5.4/series b/queue-5.4/series index 9bced4f86e5..dc8b7cf703e 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -44,3 +44,5 @@ ext4-fix-potential-infinite-loop-in-ext4_dx_readdir.patch hid-u2fzero-ignore-incomplete-packets-without-data.patch net-udp-annotate-data-race-around-udp_sk-sk-corkflag.patch net-stmmac-don-t-attach-interface-until-resume-finishes.patch +pci-fix-pci_host_bridge-struct-device-release-free-handling.patch +libnvdimm-pmem-fix-crash-triggered-when-i-o-in-flight-during-unbind.patch