libnvdimm/pfn: fix fsdax-mode namespace info-block zero-fields
authorDan Williams <dan.j.williams@intel.com>
Thu, 18 Jul 2019 22:58:36 +0000 (15:58 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 19 Jul 2019 00:08:07 +0000 (17:08 -0700)
At namespace creation time there is the potential for the "expected to
be zero" fields of a 'pfn' info-block to be filled with indeterminate
data.  While the kernel buffer is zeroed on allocation it is immediately
overwritten by nd_pfn_validate() filling it with the current contents of
the on-media info-block location.  For fields like, 'flags' and the
'padding' it potentially means that future implementations can not rely on
those fields being zero.

In preparation to stop using the 'start_pad' and 'end_trunc' fields for
section alignment, arrange for fields that are not explicitly
initialized to be guaranteed zero.  Bump the minor version to indicate
it is safe to assume the 'padding' and 'flags' are zero.  Otherwise,
this corruption is expected to benign since all other critical fields
are explicitly initialized.

Note The cc: stable is about spreading this new policy to as many
kernels as possible not fixing an issue in those kernels.  It is not
until the change titled "libnvdimm/pfn: Stop padding pmem namespaces to
section alignment" where this improper initialization becomes a problem.
So if someone decides to backport "libnvdimm/pfn: Stop padding pmem
namespaces to section alignment" (which is not tagged for stable), make
sure this pre-requisite is flagged.

Link: http://lkml.kernel.org/r/156092356065.979959.6681003754765958296.stgit@dwillia2-desk3.amr.corp.intel.com
Fixes: 32ab0a3f5170 ("libnvdimm, pmem: 'struct page' for pmem")
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Tested-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com> [ppc64]
Cc: <stable@vger.kernel.org>
Cc: David Hildenbrand <david@redhat.com>
Cc: Jane Chu <jane.chu@oracle.com>
Cc: Jeff Moyer <jmoyer@redhat.com>
Cc: Jérôme Glisse <jglisse@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Logan Gunthorpe <logang@deltatee.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@linux.ibm.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Pavel Tatashin <pasha.tatashin@soleen.com>
Cc: Toshi Kani <toshi.kani@hpe.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Wei Yang <richardw.yang@linux.intel.com>
Cc: Jason Gunthorpe <jgg@mellanox.com>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/nvdimm/dax_devs.c
drivers/nvdimm/pfn.h
drivers/nvdimm/pfn_devs.c

index 49fc18ee0565ec6cc4e72f343fcfa58ee1cdaca4..6d22b0f83b3b083901f58fddcfad41f7a604ba7c 100644 (file)
@@ -118,7 +118,7 @@ int nd_dax_probe(struct device *dev, struct nd_namespace_common *ndns)
        nvdimm_bus_unlock(&ndns->dev);
        if (!dax_dev)
                return -ENOMEM;
-       pfn_sb = devm_kzalloc(dev, sizeof(*pfn_sb), GFP_KERNEL);
+       pfn_sb = devm_kmalloc(dev, sizeof(*pfn_sb), GFP_KERNEL);
        nd_pfn->pfn_sb = pfn_sb;
        rc = nd_pfn_validate(nd_pfn, DAX_SIG);
        dev_dbg(dev, "dax: %s\n", rc == 0 ? dev_name(dax_dev) : "<none>");
index f58b849e455b1ad0bee4f08c8459049644aeab51..dfb2bcda8f5a43e570538373923684d4f7cd761a 100644 (file)
@@ -28,6 +28,7 @@ struct nd_pfn_sb {
        __le32 end_trunc;
        /* minor-version-2 record the base alignment of the mapping */
        __le32 align;
+       /* minor-version-3 guarantee the padding and flags are zero */
        u8 padding[4000];
        __le64 checksum;
 };
index 55fb6b7433ed2e7360c0f15502feab885c48fae1..06f465c0baf321a0715341c4c7555701607c490b 100644 (file)
@@ -412,6 +412,15 @@ static int nd_pfn_clear_memmap_errors(struct nd_pfn *nd_pfn)
        return 0;
 }
 
+/**
+ * nd_pfn_validate - read and validate info-block
+ * @nd_pfn: fsdax namespace runtime state / properties
+ * @sig: 'devdax' or 'fsdax' signature
+ *
+ * Upon return the info-block buffer contents (->pfn_sb) are
+ * indeterminate when validation fails, and a coherent info-block
+ * otherwise.
+ */
 int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig)
 {
        u64 checksum, offset;
@@ -557,7 +566,7 @@ int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns)
        nvdimm_bus_unlock(&ndns->dev);
        if (!pfn_dev)
                return -ENOMEM;
-       pfn_sb = devm_kzalloc(dev, sizeof(*pfn_sb), GFP_KERNEL);
+       pfn_sb = devm_kmalloc(dev, sizeof(*pfn_sb), GFP_KERNEL);
        nd_pfn = to_nd_pfn(pfn_dev);
        nd_pfn->pfn_sb = pfn_sb;
        rc = nd_pfn_validate(nd_pfn, PFN_SIG);
@@ -693,7 +702,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
        u64 checksum;
        int rc;
 
-       pfn_sb = devm_kzalloc(&nd_pfn->dev, sizeof(*pfn_sb), GFP_KERNEL);
+       pfn_sb = devm_kmalloc(&nd_pfn->dev, sizeof(*pfn_sb), GFP_KERNEL);
        if (!pfn_sb)
                return -ENOMEM;
 
@@ -702,11 +711,14 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
                sig = DAX_SIG;
        else
                sig = PFN_SIG;
+
        rc = nd_pfn_validate(nd_pfn, sig);
        if (rc != -ENODEV)
                return rc;
 
        /* no info block, do init */;
+       memset(pfn_sb, 0, sizeof(*pfn_sb));
+
        nd_region = to_nd_region(nd_pfn->dev.parent);
        if (nd_region->ro) {
                dev_info(&nd_pfn->dev,
@@ -759,7 +771,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
        memcpy(pfn_sb->uuid, nd_pfn->uuid, 16);
        memcpy(pfn_sb->parent_uuid, nd_dev_to_uuid(&ndns->dev), 16);
        pfn_sb->version_major = cpu_to_le16(1);
-       pfn_sb->version_minor = cpu_to_le16(2);
+       pfn_sb->version_minor = cpu_to_le16(3);
        pfn_sb->start_pad = cpu_to_le32(start_pad);
        pfn_sb->end_trunc = cpu_to_le32(end_trunc);
        pfn_sb->align = cpu_to_le32(nd_pfn->align);