nfit, libnvdimm: fix interleave set cookie calculation

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.

Cc: <stable@vger.kernel.org>
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>

+41 -5
+15 -1
drivers/acpi/nfit/core.c
··· 1603 + num_mappings * sizeof(struct nfit_set_info_map); 1604 } 1605 1606 - static int cmp_map(const void *m0, const void *m1) 1607 { 1608 const struct nfit_set_info_map *map0 = m0; 1609 const struct nfit_set_info_map *map1 = m1; 1610 1611 return memcmp(&map0->region_offset, &map1->region_offset, 1612 sizeof(u64)); 1613 } 1614 1615 /* Retrieve the nth entry referencing this spa */ ··· 1675 sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map), 1676 cmp_map, NULL); 1677 nd_set->cookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0); 1678 ndr_desc->nd_set = nd_set; 1679 devm_kfree(dev, info); 1680
··· 1603 + num_mappings * sizeof(struct nfit_set_info_map); 1604 } 1605 1606 + static int cmp_map_compat(const void *m0, const void *m1) 1607 { 1608 const struct nfit_set_info_map *map0 = m0; 1609 const struct nfit_set_info_map *map1 = m1; 1610 1611 return memcmp(&map0->region_offset, &map1->region_offset, 1612 sizeof(u64)); 1613 + } 1614 + 1615 + static 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 1623 /* Retrieve the nth entry referencing this spa */ ··· 1667 sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map), 1668 cmp_map, NULL); 1669 nd_set->cookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0); 1670 + 1671 + /* support namespaces created with the wrong sort order */ 1672 + sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map), 1673 + cmp_map_compat, NULL); 1674 + nd_set->altcookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0); 1675 + 1676 ndr_desc->nd_set = nd_set; 1677 devm_kfree(dev, info); 1678
+14 -4
drivers/nvdimm/namespace_devs.c
··· 1700 struct device *create_namespace_pmem(struct nd_region *nd_region, 1701 struct nd_namespace_label *nd_label) 1702 { 1703 u64 cookie = nd_region_interleave_set_cookie(nd_region); 1704 struct nd_label_ent *label_ent; 1705 struct nd_namespace_pmem *nspm; ··· 1719 if (__le64_to_cpu(nd_label->isetcookie) != cookie) { 1720 dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n", 1721 nd_label->uuid); 1722 - return ERR_PTR(-EAGAIN); 1723 } 1724 1725 nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); ··· 1738 res->name = dev_name(&nd_region->dev); 1739 res->flags = IORESOURCE_MEM; 1740 1741 - for (i = 0; i < nd_region->ndr_mappings; i++) 1742 - if (!has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i)) 1743 - break; 1744 if (i < nd_region->ndr_mappings) { 1745 struct nvdimm_drvdata *ndd = to_ndd(&nd_region->mapping[i]); 1746
··· 1700 struct device *create_namespace_pmem(struct nd_region *nd_region, 1701 struct nd_namespace_label *nd_label) 1702 { 1703 + u64 altcookie = nd_region_interleave_set_altcookie(nd_region); 1704 u64 cookie = nd_region_interleave_set_cookie(nd_region); 1705 struct nd_label_ent *label_ent; 1706 struct nd_namespace_pmem *nspm; ··· 1718 if (__le64_to_cpu(nd_label->isetcookie) != cookie) { 1719 dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n", 1720 nd_label->uuid); 1721 + if (__le64_to_cpu(nd_label->isetcookie) != altcookie) 1722 + return ERR_PTR(-EAGAIN); 1723 + 1724 + dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb\n", 1725 + nd_label->uuid); 1726 } 1727 1728 nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); ··· 1733 res->name = dev_name(&nd_region->dev); 1734 res->flags = IORESOURCE_MEM; 1735 1736 + for (i = 0; i < nd_region->ndr_mappings; i++) { 1737 + if (has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i)) 1738 + continue; 1739 + if (has_uuid_at_pos(nd_region, nd_label->uuid, altcookie, i)) 1740 + continue; 1741 + break; 1742 + } 1743 + 1744 if (i < nd_region->ndr_mappings) { 1745 struct nvdimm_drvdata *ndd = to_ndd(&nd_region->mapping[i]); 1746
+1
drivers/nvdimm/nd.h
··· 328 int nd_region_to_nstype(struct nd_region *nd_region); 329 int nd_region_register_namespaces(struct nd_region *nd_region, int *err); 330 u64 nd_region_interleave_set_cookie(struct nd_region *nd_region); 331 void nvdimm_bus_lock(struct device *dev); 332 void nvdimm_bus_unlock(struct device *dev); 333 bool is_nvdimm_bus_locked(struct device *dev);
··· 328 int nd_region_to_nstype(struct nd_region *nd_region); 329 int nd_region_register_namespaces(struct nd_region *nd_region, int *err); 330 u64 nd_region_interleave_set_cookie(struct nd_region *nd_region); 331 + u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region); 332 void nvdimm_bus_lock(struct device *dev); 333 void nvdimm_bus_unlock(struct device *dev); 334 bool is_nvdimm_bus_locked(struct device *dev);
+9
drivers/nvdimm/region_devs.c
··· 505 return 0; 506 } 507 508 void nd_mapping_free_labels(struct nd_mapping *nd_mapping) 509 { 510 struct nd_label_ent *label_ent, *e;
··· 505 return 0; 506 } 507 508 + u64 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 + 517 void nd_mapping_free_labels(struct nd_mapping *nd_mapping) 518 { 519 struct nd_label_ent *label_ent, *e;
+2
include/linux/libnvdimm.h
··· 70 71 struct nd_interleave_set { 72 u64 cookie; 73 }; 74 75 struct nd_mapping_desc {
··· 70 71 struct nd_interleave_set { 72 u64 cookie; 73 + /* compatibility with initial buggy Linux implementation */ 74 + u64 altcookie; 75 }; 76 77 struct nd_mapping_desc {