net/smc: Use a mutex for locking "struct smc_pnettable"
authorFabio M. De Francesco <fmdefrancesco@gmail.com>
Wed, 23 Feb 2022 10:02:52 +0000 (11:02 +0100)
committerJakub Kicinski <kuba@kernel.org>
Thu, 24 Feb 2022 17:09:33 +0000 (09:09 -0800)
smc_pnetid_by_table_ib() uses read_lock() and then it calls smc_pnet_apply_ib()
which, in turn, calls mutex_lock(&smc_ib_devices.mutex).

read_lock() disables preemption. Therefore, the code acquires a mutex while in
atomic context and it leads to a SAC bug.

Fix this bug by replacing the rwlock with a mutex.

Reported-and-tested-by: syzbot+4f322a6d84e991c38775@syzkaller.appspotmail.com
Fixes: 64e28b52c7a6 ("net/smc: add pnet table namespace support")
Confirmed-by: Tony Lu <tonylu@linux.alibaba.com>
Signed-off-by: Fabio M. De Francesco <fmdefrancesco@gmail.com>
Acked-by: Karsten Graul <kgraul@linux.ibm.com>
Link: https://lore.kernel.org/r/20220223100252.22562-1-fmdefrancesco@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/smc/smc_pnet.c
net/smc/smc_pnet.h

index 0599246..29f0a55 100644 (file)
@@ -113,7 +113,7 @@ static int smc_pnet_remove_by_pnetid(struct net *net, char *pnet_name)
        pnettable = &sn->pnettable;
 
        /* remove table entry */
-       write_lock(&pnettable->lock);
+       mutex_lock(&pnettable->lock);
        list_for_each_entry_safe(pnetelem, tmp_pe, &pnettable->pnetlist,
                                 list) {
                if (!pnet_name ||
@@ -131,7 +131,7 @@ static int smc_pnet_remove_by_pnetid(struct net *net, char *pnet_name)
                        rc = 0;
                }
        }
-       write_unlock(&pnettable->lock);
+       mutex_unlock(&pnettable->lock);
 
        /* if this is not the initial namespace, stop here */
        if (net != &init_net)
@@ -192,7 +192,7 @@ static int smc_pnet_add_by_ndev(struct net_device *ndev)
        sn = net_generic(net, smc_net_id);
        pnettable = &sn->pnettable;
 
-       write_lock(&pnettable->lock);
+       mutex_lock(&pnettable->lock);
        list_for_each_entry_safe(pnetelem, tmp_pe, &pnettable->pnetlist, list) {
                if (pnetelem->type == SMC_PNET_ETH && !pnetelem->ndev &&
                    !strncmp(pnetelem->eth_name, ndev->name, IFNAMSIZ)) {
@@ -206,7 +206,7 @@ static int smc_pnet_add_by_ndev(struct net_device *ndev)
                        break;
                }
        }
-       write_unlock(&pnettable->lock);
+       mutex_unlock(&pnettable->lock);
        return rc;
 }
 
@@ -224,7 +224,7 @@ static int smc_pnet_remove_by_ndev(struct net_device *ndev)
        sn = net_generic(net, smc_net_id);
        pnettable = &sn->pnettable;
 
-       write_lock(&pnettable->lock);
+       mutex_lock(&pnettable->lock);
        list_for_each_entry_safe(pnetelem, tmp_pe, &pnettable->pnetlist, list) {
                if (pnetelem->type == SMC_PNET_ETH && pnetelem->ndev == ndev) {
                        dev_put_track(pnetelem->ndev, &pnetelem->dev_tracker);
@@ -237,7 +237,7 @@ static int smc_pnet_remove_by_ndev(struct net_device *ndev)
                        break;
                }
        }
-       write_unlock(&pnettable->lock);
+       mutex_unlock(&pnettable->lock);
        return rc;
 }
 
@@ -370,7 +370,7 @@ static int smc_pnet_add_eth(struct smc_pnettable *pnettable, struct net *net,
        strncpy(new_pe->eth_name, eth_name, IFNAMSIZ);
        rc = -EEXIST;
        new_netdev = true;
-       write_lock(&pnettable->lock);
+       mutex_lock(&pnettable->lock);
        list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) {
                if (tmp_pe->type == SMC_PNET_ETH &&
                    !strncmp(tmp_pe->eth_name, eth_name, IFNAMSIZ)) {
@@ -385,9 +385,9 @@ static int smc_pnet_add_eth(struct smc_pnettable *pnettable, struct net *net,
                                             GFP_ATOMIC);
                }
                list_add_tail(&new_pe->list, &pnettable->pnetlist);
-               write_unlock(&pnettable->lock);
+               mutex_unlock(&pnettable->lock);
        } else {
-               write_unlock(&pnettable->lock);
+               mutex_unlock(&pnettable->lock);
                kfree(new_pe);
                goto out_put;
        }
@@ -448,7 +448,7 @@ static int smc_pnet_add_ib(struct smc_pnettable *pnettable, char *ib_name,
        new_pe->ib_port = ib_port;
 
        new_ibdev = true;
-       write_lock(&pnettable->lock);
+       mutex_lock(&pnettable->lock);
        list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) {
                if (tmp_pe->type == SMC_PNET_IB &&
                    !strncmp(tmp_pe->ib_name, ib_name, IB_DEVICE_NAME_MAX)) {
@@ -458,9 +458,9 @@ static int smc_pnet_add_ib(struct smc_pnettable *pnettable, char *ib_name,
        }
        if (new_ibdev) {
                list_add_tail(&new_pe->list, &pnettable->pnetlist);
-               write_unlock(&pnettable->lock);
+               mutex_unlock(&pnettable->lock);
        } else {
-               write_unlock(&pnettable->lock);
+               mutex_unlock(&pnettable->lock);
                kfree(new_pe);
        }
        return (new_ibdev) ? 0 : -EEXIST;
@@ -605,7 +605,7 @@ static int _smc_pnet_dump(struct net *net, struct sk_buff *skb, u32 portid,
        pnettable = &sn->pnettable;
 
        /* dump pnettable entries */
-       read_lock(&pnettable->lock);
+       mutex_lock(&pnettable->lock);
        list_for_each_entry(pnetelem, &pnettable->pnetlist, list) {
                if (pnetid && !smc_pnet_match(pnetelem->pnet_name, pnetid))
                        continue;
@@ -620,7 +620,7 @@ static int _smc_pnet_dump(struct net *net, struct sk_buff *skb, u32 portid,
                        break;
                }
        }
-       read_unlock(&pnettable->lock);
+       mutex_unlock(&pnettable->lock);
        return idx;
 }
 
@@ -864,7 +864,7 @@ int smc_pnet_net_init(struct net *net)
        struct smc_pnetids_ndev *pnetids_ndev = &sn->pnetids_ndev;
 
        INIT_LIST_HEAD(&pnettable->pnetlist);
-       rwlock_init(&pnettable->lock);
+       mutex_init(&pnettable->lock);
        INIT_LIST_HEAD(&pnetids_ndev->list);
        rwlock_init(&pnetids_ndev->lock);
 
@@ -944,7 +944,7 @@ static int smc_pnet_find_ndev_pnetid_by_table(struct net_device *ndev,
        sn = net_generic(net, smc_net_id);
        pnettable = &sn->pnettable;
 
-       read_lock(&pnettable->lock);
+       mutex_lock(&pnettable->lock);
        list_for_each_entry(pnetelem, &pnettable->pnetlist, list) {
                if (pnetelem->type == SMC_PNET_ETH && ndev == pnetelem->ndev) {
                        /* get pnetid of netdev device */
@@ -953,7 +953,7 @@ static int smc_pnet_find_ndev_pnetid_by_table(struct net_device *ndev,
                        break;
                }
        }
-       read_unlock(&pnettable->lock);
+       mutex_unlock(&pnettable->lock);
        return rc;
 }
 
@@ -1156,7 +1156,7 @@ int smc_pnetid_by_table_ib(struct smc_ib_device *smcibdev, u8 ib_port)
        sn = net_generic(&init_net, smc_net_id);
        pnettable = &sn->pnettable;
 
-       read_lock(&pnettable->lock);
+       mutex_lock(&pnettable->lock);
        list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) {
                if (tmp_pe->type == SMC_PNET_IB &&
                    !strncmp(tmp_pe->ib_name, ib_name, IB_DEVICE_NAME_MAX) &&
@@ -1166,7 +1166,7 @@ int smc_pnetid_by_table_ib(struct smc_ib_device *smcibdev, u8 ib_port)
                        break;
                }
        }
-       read_unlock(&pnettable->lock);
+       mutex_unlock(&pnettable->lock);
 
        return rc;
 }
@@ -1185,7 +1185,7 @@ int smc_pnetid_by_table_smcd(struct smcd_dev *smcddev)
        sn = net_generic(&init_net, smc_net_id);
        pnettable = &sn->pnettable;
 
-       read_lock(&pnettable->lock);
+       mutex_lock(&pnettable->lock);
        list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) {
                if (tmp_pe->type == SMC_PNET_IB &&
                    !strncmp(tmp_pe->ib_name, ib_name, IB_DEVICE_NAME_MAX)) {
@@ -1194,7 +1194,7 @@ int smc_pnetid_by_table_smcd(struct smcd_dev *smcddev)
                        break;
                }
        }
-       read_unlock(&pnettable->lock);
+       mutex_unlock(&pnettable->lock);
 
        return rc;
 }
index 1403927..80a88ee 100644 (file)
@@ -29,7 +29,7 @@ struct smc_link_group;
  * @pnetlist: List of PNETIDs
  */
 struct smc_pnettable {
-       rwlock_t lock;
+       struct mutex lock;
        struct list_head pnetlist;
 };