Skip to content

Commit 66dd58f

Browse files
djbwgregkh
authored andcommitted
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: 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent a084aee commit 66dd58f

5 files changed

Lines changed: 48 additions & 8 deletions

File tree

drivers/acpi/nfit.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ static size_t sizeof_nfit_set_info(int num_mappings)
965965
+ num_mappings * sizeof(struct nfit_set_info_map);
966966
}
967967

968-
static int cmp_map(const void *m0, const void *m1)
968+
static int cmp_map_compat(const void *m0, const void *m1)
969969
{
970970
const struct nfit_set_info_map *map0 = m0;
971971
const struct nfit_set_info_map *map1 = m1;
@@ -974,6 +974,14 @@ static int cmp_map(const void *m0, const void *m1)
974974
sizeof(u64));
975975
}
976976

977+
static int cmp_map(const void *m0, const void *m1)
978+
{
979+
const struct nfit_set_info_map *map0 = m0;
980+
const struct nfit_set_info_map *map1 = m1;
981+
982+
return map0->region_offset - map1->region_offset;
983+
}
984+
977985
/* Retrieve the nth entry referencing this spa */
978986
static struct acpi_nfit_memory_map *memdev_from_spa(
979987
struct acpi_nfit_desc *acpi_desc, u16 range_index, int n)
@@ -1029,6 +1037,12 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc,
10291037
sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map),
10301038
cmp_map, NULL);
10311039
nd_set->cookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
1040+
1041+
/* support namespaces created with the wrong sort order */
1042+
sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map),
1043+
cmp_map_compat, NULL);
1044+
nd_set->altcookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
1045+
10321046
ndr_desc->nd_set = nd_set;
10331047
devm_kfree(dev, info);
10341048

drivers/nvdimm/namespace_devs.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,7 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id)
15341534
static int find_pmem_label_set(struct nd_region *nd_region,
15351535
struct nd_namespace_pmem *nspm)
15361536
{
1537+
u64 altcookie = nd_region_interleave_set_altcookie(nd_region);
15371538
u64 cookie = nd_region_interleave_set_cookie(nd_region);
15381539
struct nd_namespace_label *nd_label;
15391540
u8 select_id[NSLABEL_UUID_LEN];
@@ -1542,8 +1543,10 @@ static int find_pmem_label_set(struct nd_region *nd_region,
15421543
int rc = -ENODEV, l;
15431544
u16 i;
15441545

1545-
if (cookie == 0)
1546+
if (cookie == 0) {
1547+
dev_dbg(&nd_region->dev, "invalid interleave-set-cookie\n");
15461548
return -ENXIO;
1549+
}
15471550

15481551
/*
15491552
* Find a complete set of labels by uuid. By definition we can start
@@ -1552,13 +1555,24 @@ static int find_pmem_label_set(struct nd_region *nd_region,
15521555
for_each_label(l, nd_label, nd_region->mapping[0].labels) {
15531556
u64 isetcookie = __le64_to_cpu(nd_label->isetcookie);
15541557

1555-
if (isetcookie != cookie)
1556-
continue;
1558+
if (isetcookie != cookie) {
1559+
dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n",
1560+
nd_label->uuid);
1561+
if (isetcookie != altcookie)
1562+
continue;
1563+
1564+
dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb\n",
1565+
nd_label->uuid);
1566+
}
1567+
1568+
for (i = 0; nd_region->ndr_mappings; i++) {
1569+
if (has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i))
1570+
continue;
1571+
if (has_uuid_at_pos(nd_region, nd_label->uuid, altcookie, i))
1572+
continue;
1573+
break;
1574+
}
15571575

1558-
for (i = 0; nd_region->ndr_mappings; i++)
1559-
if (!has_uuid_at_pos(nd_region, nd_label->uuid,
1560-
cookie, i))
1561-
break;
15621576
if (i < nd_region->ndr_mappings) {
15631577
/*
15641578
* Give up if we don't find an instance of a

drivers/nvdimm/nd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ struct nd_region *to_nd_region(struct device *dev);
245245
int nd_region_to_nstype(struct nd_region *nd_region);
246246
int nd_region_register_namespaces(struct nd_region *nd_region, int *err);
247247
u64 nd_region_interleave_set_cookie(struct nd_region *nd_region);
248+
u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region);
248249
void nvdimm_bus_lock(struct device *dev);
249250
void nvdimm_bus_unlock(struct device *dev);
250251
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
@@ -379,6 +379,15 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region)
379379
return 0;
380380
}
381381

382+
u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region)
383+
{
384+
struct nd_interleave_set *nd_set = nd_region->nd_set;
385+
386+
if (nd_set)
387+
return nd_set->altcookie;
388+
return 0;
389+
}
390+
382391
/*
383392
* Upon successful probe/remove, take/release a reference on the
384393
* associated interleave set (if present), and plant new btt + namespace

include/linux/libnvdimm.h

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

8484
struct nd_interleave_set {
8585
u64 cookie;
86+
/* compatibility with initial buggy Linux implementation */
87+
u64 altcookie;
8688
};
8789

8890
struct nd_region_desc {

0 commit comments

Comments
 (0)