Commit 7ff57e98 authored by Fabio M. De Francesco's avatar Fabio M. De Francesco Committed by Jakub Kicinski

net/smc: Use a mutex for locking "struct smc_pnettable"

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: 64e28b52 ("net/smc: add pnet table namespace support")
Confirmed-by: default avatarTony Lu <tonylu@linux.alibaba.com>
Signed-off-by: default avatarFabio M. De Francesco <fmdefrancesco@gmail.com>
Acked-by: default avatarKarsten Graul <kgraul@linux.ibm.com>
Link: https://lore.kernel.org/r/20220223100252.22562-1-fmdefrancesco@gmail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent e13ad144
...@@ -113,7 +113,7 @@ static int smc_pnet_remove_by_pnetid(struct net *net, char *pnet_name) ...@@ -113,7 +113,7 @@ static int smc_pnet_remove_by_pnetid(struct net *net, char *pnet_name)
pnettable = &sn->pnettable; pnettable = &sn->pnettable;
/* remove table entry */ /* remove table entry */
write_lock(&pnettable->lock); mutex_lock(&pnettable->lock);
list_for_each_entry_safe(pnetelem, tmp_pe, &pnettable->pnetlist, list_for_each_entry_safe(pnetelem, tmp_pe, &pnettable->pnetlist,
list) { list) {
if (!pnet_name || if (!pnet_name ||
...@@ -131,7 +131,7 @@ static int smc_pnet_remove_by_pnetid(struct net *net, char *pnet_name) ...@@ -131,7 +131,7 @@ static int smc_pnet_remove_by_pnetid(struct net *net, char *pnet_name)
rc = 0; rc = 0;
} }
} }
write_unlock(&pnettable->lock); mutex_unlock(&pnettable->lock);
/* if this is not the initial namespace, stop here */ /* if this is not the initial namespace, stop here */
if (net != &init_net) if (net != &init_net)
...@@ -192,7 +192,7 @@ static int smc_pnet_add_by_ndev(struct net_device *ndev) ...@@ -192,7 +192,7 @@ static int smc_pnet_add_by_ndev(struct net_device *ndev)
sn = net_generic(net, smc_net_id); sn = net_generic(net, smc_net_id);
pnettable = &sn->pnettable; pnettable = &sn->pnettable;
write_lock(&pnettable->lock); mutex_lock(&pnettable->lock);
list_for_each_entry_safe(pnetelem, tmp_pe, &pnettable->pnetlist, list) { list_for_each_entry_safe(pnetelem, tmp_pe, &pnettable->pnetlist, list) {
if (pnetelem->type == SMC_PNET_ETH && !pnetelem->ndev && if (pnetelem->type == SMC_PNET_ETH && !pnetelem->ndev &&
!strncmp(pnetelem->eth_name, ndev->name, IFNAMSIZ)) { !strncmp(pnetelem->eth_name, ndev->name, IFNAMSIZ)) {
...@@ -206,7 +206,7 @@ static int smc_pnet_add_by_ndev(struct net_device *ndev) ...@@ -206,7 +206,7 @@ static int smc_pnet_add_by_ndev(struct net_device *ndev)
break; break;
} }
} }
write_unlock(&pnettable->lock); mutex_unlock(&pnettable->lock);
return rc; return rc;
} }
...@@ -224,7 +224,7 @@ static int smc_pnet_remove_by_ndev(struct net_device *ndev) ...@@ -224,7 +224,7 @@ static int smc_pnet_remove_by_ndev(struct net_device *ndev)
sn = net_generic(net, smc_net_id); sn = net_generic(net, smc_net_id);
pnettable = &sn->pnettable; pnettable = &sn->pnettable;
write_lock(&pnettable->lock); mutex_lock(&pnettable->lock);
list_for_each_entry_safe(pnetelem, tmp_pe, &pnettable->pnetlist, list) { list_for_each_entry_safe(pnetelem, tmp_pe, &pnettable->pnetlist, list) {
if (pnetelem->type == SMC_PNET_ETH && pnetelem->ndev == ndev) { if (pnetelem->type == SMC_PNET_ETH && pnetelem->ndev == ndev) {
dev_put_track(pnetelem->ndev, &pnetelem->dev_tracker); dev_put_track(pnetelem->ndev, &pnetelem->dev_tracker);
...@@ -237,7 +237,7 @@ static int smc_pnet_remove_by_ndev(struct net_device *ndev) ...@@ -237,7 +237,7 @@ static int smc_pnet_remove_by_ndev(struct net_device *ndev)
break; break;
} }
} }
write_unlock(&pnettable->lock); mutex_unlock(&pnettable->lock);
return rc; return rc;
} }
...@@ -370,7 +370,7 @@ static int smc_pnet_add_eth(struct smc_pnettable *pnettable, struct net *net, ...@@ -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); strncpy(new_pe->eth_name, eth_name, IFNAMSIZ);
rc = -EEXIST; rc = -EEXIST;
new_netdev = true; new_netdev = true;
write_lock(&pnettable->lock); mutex_lock(&pnettable->lock);
list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) { list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) {
if (tmp_pe->type == SMC_PNET_ETH && if (tmp_pe->type == SMC_PNET_ETH &&
!strncmp(tmp_pe->eth_name, eth_name, IFNAMSIZ)) { !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, ...@@ -385,9 +385,9 @@ static int smc_pnet_add_eth(struct smc_pnettable *pnettable, struct net *net,
GFP_ATOMIC); GFP_ATOMIC);
} }
list_add_tail(&new_pe->list, &pnettable->pnetlist); list_add_tail(&new_pe->list, &pnettable->pnetlist);
write_unlock(&pnettable->lock); mutex_unlock(&pnettable->lock);
} else { } else {
write_unlock(&pnettable->lock); mutex_unlock(&pnettable->lock);
kfree(new_pe); kfree(new_pe);
goto out_put; goto out_put;
} }
...@@ -448,7 +448,7 @@ static int smc_pnet_add_ib(struct smc_pnettable *pnettable, char *ib_name, ...@@ -448,7 +448,7 @@ static int smc_pnet_add_ib(struct smc_pnettable *pnettable, char *ib_name,
new_pe->ib_port = ib_port; new_pe->ib_port = ib_port;
new_ibdev = true; new_ibdev = true;
write_lock(&pnettable->lock); mutex_lock(&pnettable->lock);
list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) { list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) {
if (tmp_pe->type == SMC_PNET_IB && if (tmp_pe->type == SMC_PNET_IB &&
!strncmp(tmp_pe->ib_name, ib_name, IB_DEVICE_NAME_MAX)) { !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, ...@@ -458,9 +458,9 @@ static int smc_pnet_add_ib(struct smc_pnettable *pnettable, char *ib_name,
} }
if (new_ibdev) { if (new_ibdev) {
list_add_tail(&new_pe->list, &pnettable->pnetlist); list_add_tail(&new_pe->list, &pnettable->pnetlist);
write_unlock(&pnettable->lock); mutex_unlock(&pnettable->lock);
} else { } else {
write_unlock(&pnettable->lock); mutex_unlock(&pnettable->lock);
kfree(new_pe); kfree(new_pe);
} }
return (new_ibdev) ? 0 : -EEXIST; return (new_ibdev) ? 0 : -EEXIST;
...@@ -605,7 +605,7 @@ static int _smc_pnet_dump(struct net *net, struct sk_buff *skb, u32 portid, ...@@ -605,7 +605,7 @@ static int _smc_pnet_dump(struct net *net, struct sk_buff *skb, u32 portid,
pnettable = &sn->pnettable; pnettable = &sn->pnettable;
/* dump pnettable entries */ /* dump pnettable entries */
read_lock(&pnettable->lock); mutex_lock(&pnettable->lock);
list_for_each_entry(pnetelem, &pnettable->pnetlist, list) { list_for_each_entry(pnetelem, &pnettable->pnetlist, list) {
if (pnetid && !smc_pnet_match(pnetelem->pnet_name, pnetid)) if (pnetid && !smc_pnet_match(pnetelem->pnet_name, pnetid))
continue; continue;
...@@ -620,7 +620,7 @@ static int _smc_pnet_dump(struct net *net, struct sk_buff *skb, u32 portid, ...@@ -620,7 +620,7 @@ static int _smc_pnet_dump(struct net *net, struct sk_buff *skb, u32 portid,
break; break;
} }
} }
read_unlock(&pnettable->lock); mutex_unlock(&pnettable->lock);
return idx; return idx;
} }
...@@ -864,7 +864,7 @@ int smc_pnet_net_init(struct net *net) ...@@ -864,7 +864,7 @@ int smc_pnet_net_init(struct net *net)
struct smc_pnetids_ndev *pnetids_ndev = &sn->pnetids_ndev; struct smc_pnetids_ndev *pnetids_ndev = &sn->pnetids_ndev;
INIT_LIST_HEAD(&pnettable->pnetlist); INIT_LIST_HEAD(&pnettable->pnetlist);
rwlock_init(&pnettable->lock); mutex_init(&pnettable->lock);
INIT_LIST_HEAD(&pnetids_ndev->list); INIT_LIST_HEAD(&pnetids_ndev->list);
rwlock_init(&pnetids_ndev->lock); rwlock_init(&pnetids_ndev->lock);
...@@ -944,7 +944,7 @@ static int smc_pnet_find_ndev_pnetid_by_table(struct net_device *ndev, ...@@ -944,7 +944,7 @@ static int smc_pnet_find_ndev_pnetid_by_table(struct net_device *ndev,
sn = net_generic(net, smc_net_id); sn = net_generic(net, smc_net_id);
pnettable = &sn->pnettable; pnettable = &sn->pnettable;
read_lock(&pnettable->lock); mutex_lock(&pnettable->lock);
list_for_each_entry(pnetelem, &pnettable->pnetlist, list) { list_for_each_entry(pnetelem, &pnettable->pnetlist, list) {
if (pnetelem->type == SMC_PNET_ETH && ndev == pnetelem->ndev) { if (pnetelem->type == SMC_PNET_ETH && ndev == pnetelem->ndev) {
/* get pnetid of netdev device */ /* get pnetid of netdev device */
...@@ -953,7 +953,7 @@ static int smc_pnet_find_ndev_pnetid_by_table(struct net_device *ndev, ...@@ -953,7 +953,7 @@ static int smc_pnet_find_ndev_pnetid_by_table(struct net_device *ndev,
break; break;
} }
} }
read_unlock(&pnettable->lock); mutex_unlock(&pnettable->lock);
return rc; return rc;
} }
...@@ -1156,7 +1156,7 @@ int smc_pnetid_by_table_ib(struct smc_ib_device *smcibdev, u8 ib_port) ...@@ -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); sn = net_generic(&init_net, smc_net_id);
pnettable = &sn->pnettable; pnettable = &sn->pnettable;
read_lock(&pnettable->lock); mutex_lock(&pnettable->lock);
list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) { list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) {
if (tmp_pe->type == SMC_PNET_IB && if (tmp_pe->type == SMC_PNET_IB &&
!strncmp(tmp_pe->ib_name, ib_name, IB_DEVICE_NAME_MAX) && !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) ...@@ -1166,7 +1166,7 @@ int smc_pnetid_by_table_ib(struct smc_ib_device *smcibdev, u8 ib_port)
break; break;
} }
} }
read_unlock(&pnettable->lock); mutex_unlock(&pnettable->lock);
return rc; return rc;
} }
...@@ -1185,7 +1185,7 @@ int smc_pnetid_by_table_smcd(struct smcd_dev *smcddev) ...@@ -1185,7 +1185,7 @@ int smc_pnetid_by_table_smcd(struct smcd_dev *smcddev)
sn = net_generic(&init_net, smc_net_id); sn = net_generic(&init_net, smc_net_id);
pnettable = &sn->pnettable; pnettable = &sn->pnettable;
read_lock(&pnettable->lock); mutex_lock(&pnettable->lock);
list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) { list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) {
if (tmp_pe->type == SMC_PNET_IB && if (tmp_pe->type == SMC_PNET_IB &&
!strncmp(tmp_pe->ib_name, ib_name, IB_DEVICE_NAME_MAX)) { !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) ...@@ -1194,7 +1194,7 @@ int smc_pnetid_by_table_smcd(struct smcd_dev *smcddev)
break; break;
} }
} }
read_unlock(&pnettable->lock); mutex_unlock(&pnettable->lock);
return rc; return rc;
} }
...@@ -29,7 +29,7 @@ struct smc_link_group; ...@@ -29,7 +29,7 @@ struct smc_link_group;
* @pnetlist: List of PNETIDs * @pnetlist: List of PNETIDs
*/ */
struct smc_pnettable { struct smc_pnettable {
rwlock_t lock; struct mutex lock;
struct list_head pnetlist; struct list_head pnetlist;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment