s390/dasd: fix Oops in dasd_alias_get_start_dev due to missing pavgroup
authorStefan Haberland <sth@linux.ibm.com>
Mon, 19 Sep 2022 15:49:31 +0000 (17:49 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 28 Sep 2022 08:55:46 +0000 (10:55 +0200)
commit db7ba07108a48c0f95b74fabbfd5d63e924f992d upstream.

Fix Oops in dasd_alias_get_start_dev() function caused by the pavgroup
pointer being NULL.

The pavgroup pointer is checked on the entrance of the function but
without the lcu->lock being held. Therefore there is a race window
between dasd_alias_get_start_dev() and _lcu_update() which sets
pavgroup to NULL with the lcu->lock held.

Fix by checking the pavgroup pointer with lcu->lock held.

Cc: <stable@vger.kernel.org> # 2.6.25+
Fixes: 8e09f21574ea ("[S390] dasd: add hyper PAV support to DASD device driver, part 1")
Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Jan Hoeppner <hoeppner@linux.ibm.com>
Link: https://lore.kernel.org/r/20220919154931.4123002-2-sth@linux.ibm.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/s390/block/dasd_alias.c

index 2002684a68b3c9a668891a6d849b45057b474076..53eb32c65abb98704ced193906e5012845531b7c 100644 (file)
@@ -674,12 +674,12 @@ int dasd_alias_remove_device(struct dasd_device *device)
 struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device)
 {
        struct dasd_eckd_private *alias_priv, *private = base_device->private;
-       struct alias_pav_group *group = private->pavgroup;
        struct alias_lcu *lcu = private->lcu;
        struct dasd_device *alias_device;
+       struct alias_pav_group *group;
        unsigned long flags;
 
-       if (!group || !lcu)
+       if (!lcu)
                return NULL;
        if (lcu->pav == NO_PAV ||
            lcu->flags & (NEED_UAC_UPDATE | UPDATE_PENDING))
@@ -696,6 +696,11 @@ struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device)
        }
 
        spin_lock_irqsave(&lcu->lock, flags);
+       group = private->pavgroup;
+       if (!group) {
+               spin_unlock_irqrestore(&lcu->lock, flags);
+               return NULL;
+       }
        alias_device = group->next;
        if (!alias_device) {
                if (list_empty(&group->aliaslist)) {