lkml.org 
[lkml]   [2021]   [Feb]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.4 36/65] libnvdimm/dimm: Avoid race between probe and available_slots_show()
    Date
    From: Dan Williams <dan.j.williams@intel.com>

    commit 7018c897c2f243d4b5f1b94bc6b4831a7eab80fb upstream.

    Richard reports that the following test:

    (while true; do
    cat /sys/bus/nd/devices/nmem*/available_slots 2>&1 > /dev/null
    done) &

    while true; do
    for i in $(seq 0 4); do
    echo nmem$i > /sys/bus/nd/drivers/nvdimm/bind
    done
    for i in $(seq 0 4); do
    echo nmem$i > /sys/bus/nd/drivers/nvdimm/unbind
    done
    done

    ...fails with a crash signature like:

    divide error: 0000 [#1] SMP KASAN PTI
    RIP: 0010:nd_label_nfree+0x134/0x1a0 [libnvdimm]
    [..]
    Call Trace:
    available_slots_show+0x4e/0x120 [libnvdimm]
    dev_attr_show+0x42/0x80
    ? memset+0x20/0x40
    sysfs_kf_seq_show+0x218/0x410

    The root cause is that available_slots_show() consults driver-data, but
    fails to synchronize against device-unbind setting up a TOCTOU race to
    access uninitialized memory.

    Validate driver-data under the device-lock.

    Fixes: 4d88a97aa9e8 ("libnvdimm, nvdimm: dimm driver and base libnvdimm device-driver infrastructure")
    Cc: <stable@vger.kernel.org>
    Cc: Vishal Verma <vishal.l.verma@intel.com>
    Cc: Dave Jiang <dave.jiang@intel.com>
    Cc: Ira Weiny <ira.weiny@intel.com>
    Cc: Coly Li <colyli@suse.com>
    Reported-by: Richard Palethorpe <rpalethorpe@suse.com>
    Acked-by: Richard Palethorpe <rpalethorpe@suse.com>
    Signed-off-by: Dan Williams <dan.j.williams@intel.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    drivers/nvdimm/dimm_devs.c | 18 +++++++++++++++---
    1 file changed, 15 insertions(+), 3 deletions(-)

    --- a/drivers/nvdimm/dimm_devs.c
    +++ b/drivers/nvdimm/dimm_devs.c
    @@ -344,16 +344,16 @@ static ssize_t state_show(struct device
    }
    static DEVICE_ATTR_RO(state);

    -static ssize_t available_slots_show(struct device *dev,
    - struct device_attribute *attr, char *buf)
    +static ssize_t __available_slots_show(struct nvdimm_drvdata *ndd, char *buf)
    {
    - struct nvdimm_drvdata *ndd = dev_get_drvdata(dev);
    + struct device *dev;
    ssize_t rc;
    u32 nfree;

    if (!ndd)
    return -ENXIO;

    + dev = ndd->dev;
    nvdimm_bus_lock(dev);
    nfree = nd_label_nfree(ndd);
    if (nfree - 1 > nfree) {
    @@ -365,6 +365,18 @@ static ssize_t available_slots_show(stru
    nvdimm_bus_unlock(dev);
    return rc;
    }
    +
    +static ssize_t available_slots_show(struct device *dev,
    + struct device_attribute *attr, char *buf)
    +{
    + ssize_t rc;
    +
    + nd_device_lock(dev);
    + rc = __available_slots_show(dev_get_drvdata(dev), buf);
    + nd_device_unlock(dev);
    +
    + return rc;
    +}
    static DEVICE_ATTR_RO(available_slots);

    __weak ssize_t security_show(struct device *dev,

    \
     
     \ /
      Last update: 2021-02-08 17:37    [W:8.800 / U:0.008 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site