From: John Groves <[email protected]>

dax_holder_notify_failure() reads dax_dev->holder_ops twice without
READ_ONCE() -- once for the NULL check and once for the indirect
notify_failure() call. A concurrent fs_put_dax() can clear holder_ops
between the two reads, so the check can observe a non-NULL pointer while
the call dereferences NULL. (kill_dax() also clears holder_ops, but only
after synchronize_srcu(), so it cannot race a reader that is inside
dax_read_lock(); fs_put_dax() does no such synchronization.)

Fetch holder_ops once into a local with READ_ONCE() so the NULL check and
the indirect call observe the same value.

Fixes: 8012b86608552 ("dax: introduce holder for dax_device")
Suggested-by: Richard Cheng <[email protected]>
Reviewed-by: Richard Cheng <[email protected]>
Signed-off-by: John Groves <[email protected]>
---
 drivers/dax/super.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/dax/super.c b/drivers/dax/super.c
index 25cf99dd9360b..433cd431a6c06 100644
--- a/drivers/dax/super.c
+++ b/drivers/dax/super.c
@@ -303,6 +303,7 @@ EXPORT_SYMBOL_GPL(dax_recovery_write);
 int dax_holder_notify_failure(struct dax_device *dax_dev, u64 off,
                              u64 len, int mf_flags)
 {
+       const struct dax_holder_operations *ops;
        int rc, id;
 
        id = dax_read_lock();
@@ -311,12 +312,19 @@ int dax_holder_notify_failure(struct dax_device *dax_dev, 
u64 off,
                goto out;
        }
 
-       if (!dax_dev->holder_ops) {
+       /*
+        * Read holder_ops once: a concurrent fs_put_dax() can clear it without
+        * synchronizing against readers. Without the single fetch the compiler
+        * could reload between the NULL check and the call and dereference a
+        * NULL ops.
+        */
+       ops = READ_ONCE(dax_dev->holder_ops);
+       if (!ops) {
                rc = -EOPNOTSUPP;
                goto out;
        }
 
-       rc = dax_dev->holder_ops->notify_failure(dax_dev, off, len, mf_flags);
+       rc = ops->notify_failure(dax_dev, off, len, mf_flags);
 out:
        dax_read_unlock(id);
        return rc;
-- 
2.53.0



Reply via email to