From 855a46681e0ba601952c346e5b62d867e51ecb23 Mon Sep 17 00:00:00 2001 From: Hemanth Selam Date: Fri, 11 Sep 2026 13:55:43 +0530 Subject: [PATCH 1/6] nvdimm/pmem: Release gendisk on probe failure pmem_attach_disk() allocates the gendisk with blk_alloc_disk() and hands it to devres only once device_add_disk() has succeeded. Until that point the probe path owns the disk itself, which is why every failure after the allocation jumps to the out: label and puts it there. The devm_init_badblocks() failure returns directly instead, so the disk allocated a few lines earlier is never released. Nothing releases it afterwards either: the devres action that would have done so has not been registered yet, so unbinding the namespace or destroying it does not reach the disk, and it stays allocated along with its queue and its bdev inode until the machine is rebooted. devm_init_badblocks() only fails when a single page allocation fails, so reaching this at all needs memory exhaustion during namespace probe, and because device_add_disk() has not run there is nothing user visible left behind: no device node, no sysfs entry, only the leaked memory. Release the gendisk through the existing cleanup path on this failure. Fixes: 3dd60fb9d95d ("nvdimm/pmem: stop using q_usage_count as external pgmap refcount") Signed-off-by: Hemanth Selam Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260911082543.473670-1-hemanth.selam@gmail.com Signed-off-by: Alison Schofield --- drivers/nvdimm/pmem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index 30a51c365ce8..648fc7d66063 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -563,8 +563,10 @@ static int pmem_attach_disk(struct device *dev, nvdimm_namespace_disk_name(ndns, disk->disk_name); set_capacity(disk, (pmem->size - pmem->pfn_pad - pmem->data_offset) / 512); - if (devm_init_badblocks(dev, &pmem->bb)) - return -ENOMEM; + if (devm_init_badblocks(dev, &pmem->bb)) { + rc = -ENOMEM; + goto out; + } nvdimm_badblocks_populate(nd_region, &pmem->bb, &bb_range); disk->bb = &pmem->bb; From c0962f508e80d73aab8406b716496d40eb4bfc14 Mon Sep 17 00:00:00 2001 From: Serhat Kumral Date: Fri, 2 Oct 2026 19:56:41 +0300 Subject: [PATCH 2/6] libnvdimm: Handle negative return from nvdimm_clear_poison() in nsio_rw_bytes() When nvdimm_clear_poison() fails, it returns a negative error code. However, 'cleared' is a signed long while 'size' is size_t. The comparison 'cleared < size' promotes 'cleared' to an unsigned long. So the error condition can be bypassed and the function can incorrectly return success if the subsequent flush succeeds. Explicitly check for negative error codes before comparing with 'size'. Fixes: 868f036fee4b ("libnvdimm: fix mishandled nvdimm_clear_poison() return value") Assisted-by: LLM Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/90d3d353-28e9-4f6d-b141-a9b7157d5514@moroto.mountain/ Signed-off-by: Serhat Kumral Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20261002165641.14479-1-serhatkumral1@gmail.com Signed-off-by: Alison Schofield --- drivers/nvdimm/claim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c index 309cd2cddb0e..2725dceaf938 100644 --- a/drivers/nvdimm/claim.c +++ b/drivers/nvdimm/claim.c @@ -263,7 +263,7 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns, might_sleep(); cleared = nvdimm_clear_poison(&ndns->dev, nsio->res.start + offset, size); - if (cleared < size) + if (cleared < 0 || cleared < size) rc = -EIO; if (cleared > 0 && cleared / 512) { cleared /= 512; From 4da700e2cadf84a6e858391d5be51d64dcba080f Mon Sep 17 00:00:00 2001 From: Jianyun Gao Date: Mon, 12 Oct 2026 23:18:57 +0800 Subject: [PATCH 3/6] dax: Return errors from dax_core_init() dax_core_init() returns success when either the character device number allocation or DAX bus registration fails, even though the error paths have already released the initialized resources. For modular DAX, this allows the module to remain loaded despite failed initialization, leaving subsequent users and module removal to access resources that have already been released. Return the error code from the failed operation so that module loading fails rather than reporting successful initialization after the resources have been released. Fixes: 9567da0b408a ("device-dax: Introduce bus + driver model") Cc: stable@vger.kernel.org Signed-off-by: Jianyun Gao Reviewed-by: Gregory Price (Meta) Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20261012151857.22534-1-jianyungao89@gmail.com Signed-off-by: Alison Schofield --- drivers/dax/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dax/super.c b/drivers/dax/super.c index 45f84b0eb909..210f4af479f0 100644 --- a/drivers/dax/super.c +++ b/drivers/dax/super.c @@ -738,7 +738,7 @@ static int __init dax_core_init(void) unregister_chrdev_region(dax_devt, MINORMASK+1); err_chrdev: dax_fs_exit(); - return 0; + return rc; } static void __exit dax_core_exit(void) From a4bfbfb4e98a62b589ac7ca9e73e9612bfe8cab7 Mon Sep 17 00:00:00 2001 From: Jianyun Gao Date: Sun, 11 Oct 2026 14:48:39 +0800 Subject: [PATCH 4/6] dax: fix dax_range_compare_iter() to use correct size and address offset dax_range_compare_iter() has two bugs in how it calls dax_iomap_direct_access() and uses the returned addresses: 1. The size argument passed to dax_iomap_direct_access() is ALIGN(pos + len, PAGE_SIZE), which is an absolute file offset rather than a length. This causes the underlying dax_direct_access() to request a massively inflated nr_pages via PHYS_PFN(size). 2. The addresses returned by dax_iomap_direct_access() are page-aligned starting addresses, but memcmp() compares from saddr/daddr directly without adding the page-in-page offset of pos1/pos2. When pos1/pos2 are not page-aligned, this compares the wrong data. Fix the size by computing ALIGN(len + offset_in_page(pos), PAGE_SIZE), matching the pattern used in dax_iomap_copy_around(). Fix the address by adding offset_in_page(pos1)/offset_in_page(pos2) to saddr/daddr in the memcmp() call. Fixes: 6f7db3894ae2 ("fsdax: dedup file range to use a compare function") Cc: stable@vger.kernel.org Signed-off-by: Jianyun Gao Reviewed-by: Jan Kara Link: https://patch.msgid.link/20261011064840.1296-1-jianyungao89@gmail.com Signed-off-by: Alison Schofield --- fs/dax.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/dax.c b/fs/dax.c index 1fbba0d21c13..dbb58692efec 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -2200,6 +2200,7 @@ static int dax_range_compare_iter(struct iomap_iter *it_src, const struct iomap *dmap = &it_dest->iomap; loff_t pos1 = it_src->pos, pos2 = it_dest->pos; void *saddr, *daddr; + size_t size1, size2; int id, ret; len = min(len, min(smap->length, dmap->length)); @@ -2215,17 +2216,18 @@ static int dax_range_compare_iter(struct iomap_iter *it_src, } id = dax_read_lock(); - ret = dax_iomap_direct_access(smap, pos1, ALIGN(pos1 + len, PAGE_SIZE), - &saddr, NULL); + size1 = ALIGN(len + offset_in_page(pos1), PAGE_SIZE); + ret = dax_iomap_direct_access(smap, pos1, size1, &saddr, NULL); if (ret < 0) goto out_unlock; - ret = dax_iomap_direct_access(dmap, pos2, ALIGN(pos2 + len, PAGE_SIZE), - &daddr, NULL); + size2 = ALIGN(len + offset_in_page(pos2), PAGE_SIZE); + ret = dax_iomap_direct_access(dmap, pos2, size2, &daddr, NULL); if (ret < 0) goto out_unlock; - *same = !memcmp(saddr, daddr, len); + *same = !memcmp(saddr + offset_in_page(pos1), + daddr + offset_in_page(pos2), len); if (!*same) len = 0; dax_read_unlock(id); From df6cc0a24717c38ac81b5ba1e839c35e74e667dc Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Thu, 24 Sep 2026 10:50:34 +0800 Subject: [PATCH 5/6] dax/bus: Return errors for invalid mapping targets After the DAX configuration locking was converted to rwsems, successful lock acquisition leaves rc set to zero in mapping_store(). Two error paths can return this stale value: an unbound region driver and a misaligned range size. The sysfs write therefore reports success without allocating the requested range. Return -ENXIO when the region driver is not bound and -EINVAL when the range size is misaligned. Fixes: c05ae9d85b47 ("dax/bus.c: replace driver-core lock usage by a local rwsem") Assisted-by: LLM Signed-off-by: Muchun Song Reviewed-by: Dave Jiang Reviewed-by: Jianyun Gao Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260924025035.1265035-2-songmuchun@bytedance.com Signed-off-by: Alison Schofield --- drivers/dax/bus.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index b809e1a264af..e40c25401cf0 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -1192,7 +1192,7 @@ static ssize_t mapping_store(struct device *dev, struct device_attribute *attr, return rc; if (!dax_region->dev->driver) { up_write(&dax_region_rwsem); - return rc; + return -ENXIO; } rc = down_write_killable(&dax_dev_rwsem); if (rc) { @@ -1201,8 +1201,12 @@ static ssize_t mapping_store(struct device *dev, struct device_attribute *attr, } to_alloc = range_len(&r); - if (alloc_is_aligned(dev_dax, to_alloc)) + if (!alloc_is_aligned(dev_dax, to_alloc)) { + dev_dbg(dev, "%s: size: %zu misaligned\n", __func__, to_alloc); + rc = -EINVAL; + } else { rc = alloc_dev_dax_range(dev_dax, r.start, to_alloc); + } up_write(&dax_dev_rwsem); up_write(&dax_region_rwsem); From 9b036546a02c6c9a5133f9c1e93503896595c6ba Mon Sep 17 00:00:00 2001 From: Jianyun Gao Date: Thu, 15 Oct 2026 22:34:25 +0800 Subject: [PATCH 6/6] dax: fix wrong error code returned from dax_dedupe_file_range_compare() When dax_range_compare_iter() returns a negative error code (e.g. -EIO), dax_dedupe_file_range_compare() incorrectly returns 'ret' which holds the positive return value from iomap_iter() indicating more iterations remain. This positive value leaks to the caller, where vfs_dedupe_file_range() interprets it as success (bytes_deduped = len), causing userspace to incorrectly believe deduplication completed. Return 'status' instead to propagate the actual error code. Fixes: 0e79e3736d54 ("fsdax: dedupe: iter two files at the same time") Cc: stable@vger.kernel.org Signed-off-by: Jianyun Gao Reviewed-by: Jan Kara Reviewed-by: "Darrick J. Wong" Link: https://patch.msgid.link/20261015143426.17620-1-jianyungao89@gmail.com Signed-off-by: Alison Schofield --- fs/dax.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/dax.c b/fs/dax.c index dbb58692efec..61dce4601aaa 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -2266,7 +2266,7 @@ int dax_dedupe_file_range_compare(struct inode *src, loff_t srcoff, status = dax_range_compare_iter(&src_iter, &dst_iter, min(src_iter.len, dst_iter.len), same); if (status < 0) - return ret; + return status; src_iter.status = dst_iter.status = status; } return ret;