Skip to content

Commit 86ef58a

Browse files
committed
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: eaf9615 ("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>
1 parent df06a2d commit 86ef58a

File tree

5 files changed

+41
-5
lines changed

5 files changed

+41
-5
lines changed

drivers/acpi/nfit/core.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1603,7 +1603,7 @@ static size_t sizeof_nfit_set_info(int num_mappings)
16031603
+ num_mappings * sizeof(struct nfit_set_info_map);
16041604
}
16051605

1606-
static int cmp_map(const void *m0, const void *m1)
1606+
static int cmp_map_compat(const void *m0, const void *m1)
16071607
{
16081608
const struct nfit_set_info_map *map0 = m0;
16091609
const struct nfit_set_info_map *map1 = m1;
@@ -1612,6 +1612,14 @@ static int cmp_map(const void *m0, const void *m1)
16121612
sizeof(u64));
16131613
}
16141614

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+
16151623
/* Retrieve the nth entry referencing this spa */
16161624
static struct acpi_nfit_memory_map *memdev_from_spa(
16171625
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,
16671675
sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map),
16681676
cmp_map, NULL);
16691677
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+
16701684
ndr_desc->nd_set = nd_set;
16711685
devm_kfree(dev, info);
16721686

drivers/nvdimm/namespace_devs.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,6 +1700,7 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id)
17001700
struct device *create_namespace_pmem(struct nd_region *nd_region,
17011701
struct nd_namespace_label *nd_label)
17021702
{
1703+
u64 altcookie = nd_region_interleave_set_altcookie(nd_region);
17031704
u64 cookie = nd_region_interleave_set_cookie(nd_region);
17041705
struct nd_label_ent *label_ent;
17051706
struct nd_namespace_pmem *nspm;
@@ -1718,7 +1719,11 @@ struct device *create_namespace_pmem(struct nd_region *nd_region,
17181719
if (__le64_to_cpu(nd_label->isetcookie) != cookie) {
17191720
dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n",
17201721
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);
17221727
}
17231728

17241729
nspm = kzalloc(sizeof(*nspm), GFP_KERNEL);
@@ -1733,9 +1738,14 @@ struct device *create_namespace_pmem(struct nd_region *nd_region,
17331738
res->name = dev_name(&nd_region->dev);
17341739
res->flags = IORESOURCE_MEM;
17351740

1736-
for (i = 0; i < nd_region->ndr_mappings; i++)
1737-
if (!has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i))
1738-
break;
1741+
for (i = 0; i < nd_region->ndr_mappings; i++) {
1742+
if (has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i))
1743+
continue;
1744+
if (has_uuid_at_pos(nd_region, nd_label->uuid, altcookie, i))
1745+
continue;
1746+
break;
1747+
}
1748+
17391749
if (i < nd_region->ndr_mappings) {
17401750
struct nvdimm_drvdata *ndd = to_ndd(&nd_region->mapping[i]);
17411751

drivers/nvdimm/nd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ struct nd_region *to_nd_region(struct device *dev);
328328
int nd_region_to_nstype(struct nd_region *nd_region);
329329
int nd_region_register_namespaces(struct nd_region *nd_region, int *err);
330330
u64 nd_region_interleave_set_cookie(struct nd_region *nd_region);
331+
u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region);
331332
void nvdimm_bus_lock(struct device *dev);
332333
void nvdimm_bus_unlock(struct device *dev);
333334
bool is_nvdimm_bus_locked(struct device *dev);

drivers/nvdimm/region_devs.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,15 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region)
505505
return 0;
506506
}
507507

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+
508517
void nd_mapping_free_labels(struct nd_mapping *nd_mapping)
509518
{
510519
struct nd_label_ent *label_ent, *e;

include/linux/libnvdimm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ struct nd_cmd_desc {
7070

7171
struct nd_interleave_set {
7272
u64 cookie;
73+
/* compatibility with initial buggy Linux implementation */
74+
u64 altcookie;
7375
};
7476

7577
struct nd_mapping_desc {

0 commit comments

Comments
 (0)