aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2017-02-28 21:32:48 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-03-14 22:02:47 -0400
commit9ad1571da2c0139db952d8df5a9e4d67a752948b (patch)
tree1c897157243e054ba148e2f09eef4ab1c2d81742
parenta7b9c9ddb6f457bab2fc8527ffccf16eefba4af4 (diff)
nfit, libnvdimm: fix interleave set cookie calculation
commit 86ef58a4e35e8fa66afb5898cf6dec6a3bb29f67 upstream. The interleave-set cookie is a sum that sanity checks the composition of an interleave set has not changed from when the namespace was initially created. The checksum is calculated by sorting the DIMMs by their location in the interleave-set. The comparison for the sort must be 64-bit wide, not byte-by-byte as performed by memcmp() in the broken case. Fix the implementation to accept correct cookie values in addition to the Linux "memcmp" order cookies, but only allow correct cookies to be generated going forward. It does mean that namespaces created by third-party-tooling, or created by newer kernels with this fix, will not validate on older kernels. However, there are a couple mitigating conditions: 1/ platforms with namespace-label capable NVDIMMs are not widely available. 2/ interleave-sets with a single-dimm are by definition not affected (nothing to sort). This covers the QEMU-KVM NVDIMM emulation case. The cookie stored in the namespace label will be fixed by any write the namespace label, the most straightforward way to achieve this is to write to the "alt_name" attribute of a namespace in sysfs. Fixes: eaf961536e16 ("libnvdimm, nfit: add interleave-set state-tracking infrastructure") Reported-by: Nicholas Moulin <nicholas.w.moulin@linux.intel.com> Tested-by: Nicholas Moulin <nicholas.w.moulin@linux.intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/acpi/nfit/core.c16
-rw-r--r--drivers/nvdimm/namespace_devs.c18
-rw-r--r--drivers/nvdimm/nd.h1
-rw-r--r--drivers/nvdimm/region_devs.c9
-rw-r--r--include/linux/libnvdimm.h2
5 files changed, 41 insertions, 5 deletions
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index 6eb6733a7a5c..d1664df001f8 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -1603,7 +1603,7 @@ static size_t sizeof_nfit_set_info(int num_mappings)
1603 + num_mappings * sizeof(struct nfit_set_info_map); 1603 + num_mappings * sizeof(struct nfit_set_info_map);
1604} 1604}
1605 1605
1606static int cmp_map(const void *m0, const void *m1) 1606static int cmp_map_compat(const void *m0, const void *m1)
1607{ 1607{
1608 const struct nfit_set_info_map *map0 = m0; 1608 const struct nfit_set_info_map *map0 = m0;
1609 const struct nfit_set_info_map *map1 = m1; 1609 const struct nfit_set_info_map *map1 = m1;
@@ -1612,6 +1612,14 @@ static int cmp_map(const void *m0, const void *m1)
1612 sizeof(u64)); 1612 sizeof(u64));
1613} 1613}
1614 1614
1615static int cmp_map(const void *m0, const void *m1)
1616{
1617 const struct nfit_set_info_map *map0 = m0;
1618 const struct nfit_set_info_map *map1 = m1;
1619
1620 return map0->region_offset - map1->region_offset;
1621}
1622
1615/* Retrieve the nth entry referencing this spa */ 1623/* Retrieve the nth entry referencing this spa */
1616static struct acpi_nfit_memory_map *memdev_from_spa( 1624static struct acpi_nfit_memory_map *memdev_from_spa(
1617 struct acpi_nfit_desc *acpi_desc, u16 range_index, int n) 1625 struct acpi_nfit_desc *acpi_desc, u16 range_index, int n)
@@ -1667,6 +1675,12 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc,
1667 sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map), 1675 sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map),
1668 cmp_map, NULL); 1676 cmp_map, NULL);
1669 nd_set->cookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0); 1677 nd_set->cookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
1678
1679 /* support namespaces created with the wrong sort order */
1680 sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map),
1681 cmp_map_compat, NULL);
1682 nd_set->altcookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
1683
1670 ndr_desc->nd_set = nd_set; 1684 ndr_desc->nd_set = nd_set;
1671 devm_kfree(dev, info); 1685 devm_kfree(dev, info);
1672 1686
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index aefca644219b..a38ae34b74e4 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -1700,6 +1700,7 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id)
1700struct device *create_namespace_pmem(struct nd_region *nd_region, 1700struct device *create_namespace_pmem(struct nd_region *nd_region,
1701 struct nd_namespace_label *nd_label) 1701 struct nd_namespace_label *nd_label)
1702{ 1702{
1703 u64 altcookie = nd_region_interleave_set_altcookie(nd_region);
1703 u64 cookie = nd_region_interleave_set_cookie(nd_region); 1704 u64 cookie = nd_region_interleave_set_cookie(nd_region);
1704 struct nd_label_ent *label_ent; 1705 struct nd_label_ent *label_ent;
1705 struct nd_namespace_pmem *nspm; 1706 struct nd_namespace_pmem *nspm;
@@ -1718,7 +1719,11 @@ struct device *create_namespace_pmem(struct nd_region *nd_region,
1718 if (__le64_to_cpu(nd_label->isetcookie) != cookie) { 1719 if (__le64_to_cpu(nd_label->isetcookie) != cookie) {
1719 dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n", 1720 dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n",
1720 nd_label->uuid); 1721 nd_label->uuid);
1721 return ERR_PTR(-EAGAIN); 1722 if (__le64_to_cpu(nd_label->isetcookie) != altcookie)
1723 return ERR_PTR(-EAGAIN);
1724
1725 dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb\n",
1726 nd_label->uuid);
1722 } 1727 }
1723 1728
1724 nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); 1729 nspm = kzalloc(sizeof(*nspm), GFP_KERNEL);
@@ -1733,9 +1738,14 @@ struct device *create_namespace_pmem(struct nd_region *nd_region,
1733 res->name = dev_name(&nd_region->dev); 1738 res->name = dev_name(&nd_region->dev);
1734 res->flags = IORESOURCE_MEM; 1739 res->flags = IORESOURCE_MEM;
1735 1740
1736 for (i = 0; i < nd_region->ndr_mappings; i++) 1741 for (i = 0; i < nd_region->ndr_mappings; i++) {
1737 if (!has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i)) 1742 if (has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i))
1738 break; 1743 continue;
1744 if (has_uuid_at_pos(nd_region, nd_label->uuid, altcookie, i))
1745 continue;
1746 break;
1747 }
1748
1739 if (i < nd_region->ndr_mappings) { 1749 if (i < nd_region->ndr_mappings) {
1740 struct nvdimm_drvdata *ndd = to_ndd(&nd_region->mapping[i]); 1750 struct nvdimm_drvdata *ndd = to_ndd(&nd_region->mapping[i]);
1741 1751
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index d3b2fca8deec..d869236b474f 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -327,6 +327,7 @@ struct nd_region *to_nd_region(struct device *dev);
327int nd_region_to_nstype(struct nd_region *nd_region); 327int nd_region_to_nstype(struct nd_region *nd_region);
328int nd_region_register_namespaces(struct nd_region *nd_region, int *err); 328int nd_region_register_namespaces(struct nd_region *nd_region, int *err);
329u64 nd_region_interleave_set_cookie(struct nd_region *nd_region); 329u64 nd_region_interleave_set_cookie(struct nd_region *nd_region);
330u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region);
330void nvdimm_bus_lock(struct device *dev); 331void nvdimm_bus_lock(struct device *dev);
331void nvdimm_bus_unlock(struct device *dev); 332void nvdimm_bus_unlock(struct device *dev);
332bool is_nvdimm_bus_locked(struct device *dev); 333bool is_nvdimm_bus_locked(struct device *dev);
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index 6af5e629140c..9cf6f1a88fce 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -505,6 +505,15 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region)
505 return 0; 505 return 0;
506} 506}
507 507
508u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region)
509{
510 struct nd_interleave_set *nd_set = nd_region->nd_set;
511
512 if (nd_set)
513 return nd_set->altcookie;
514 return 0;
515}
516
508void nd_mapping_free_labels(struct nd_mapping *nd_mapping) 517void nd_mapping_free_labels(struct nd_mapping *nd_mapping)
509{ 518{
510 struct nd_label_ent *label_ent, *e; 519 struct nd_label_ent *label_ent, *e;
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index 8458c5351e56..77e7af32543f 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -70,6 +70,8 @@ struct nd_cmd_desc {
70 70
71struct nd_interleave_set { 71struct nd_interleave_set {
72 u64 cookie; 72 u64 cookie;
73 /* compatibility with initial buggy Linux implementation */
74 u64 altcookie;
73}; 75};
74 76
75struct nd_mapping_desc { 77struct nd_mapping_desc {