scsi: ch: synchronize ch_probe() and ch_open()
authorHannes Reinecke <hare@suse.de>
Thu, 13 Feb 2020 15:32:06 +0000 (16:32 +0100)
committerMartin K. Petersen <martin.petersen@oracle.com>
Mon, 24 Feb 2020 19:54:25 +0000 (14:54 -0500)
The 'ch' device node is created before the configuration is being read in,
which leads to a race window when ch_open() is called before that.

To avoid any races we should be taking the device mutex during
ch_readconfig() and ch_init_elem(), and also during ch_open().
That ensures ch_probe is finished before ch_open() completes.

Link: https://lore.kernel.org/r/20200213153207.123357-3-hare@suse.de
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/ch.c

index e3a006d..7fdb636 100644 (file)
@@ -606,7 +606,10 @@ ch_open(struct inode *inode, struct file *file)
                mutex_unlock(&ch_mutex);
                return -ENXIO;
        }
+       /* Synchronize with ch_probe() */
+       mutex_lock(&ch->lock);
        file->private_data = ch;
+       mutex_unlock(&ch->lock);
        mutex_unlock(&ch_mutex);
        return 0;
 }
@@ -949,6 +952,9 @@ static int ch_probe(struct device *dev)
                goto remove_idr;
        }
 
+       mutex_init(&ch->lock);
+       kref_init(&ch->ref);
+       ch->device = sd;
        class_dev = device_create(ch_sysfs_class, dev,
                                  MKDEV(SCSI_CHANGER_MAJOR, ch->minor), ch,
                                  "s%s", ch->name);
@@ -959,15 +965,16 @@ static int ch_probe(struct device *dev)
                goto put_device;
        }
 
-       mutex_init(&ch->lock);
-       kref_init(&ch->ref);
-       ch->device = sd;
+       mutex_lock(&ch->lock);
        ret = ch_readconfig(ch);
-       if (ret)
+       if (ret) {
+               mutex_unlock(&ch->lock);
                goto destroy_dev;
+       }
        if (init)
                ch_init_elem(ch);
 
+       mutex_unlock(&ch->lock);
        dev_set_drvdata(dev, ch);
        sdev_printk(KERN_INFO, sd, "Attached scsi changer %s\n", ch->name);