net/smc: Use a mutex for locking "struct smc_pnettable"
commit7ff57e98fb
upstream. 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:64e28b52c7
("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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
e96e204ee6
Коммит
062772d5cc
|
@ -112,7 +112,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 ||
|
||||
|
@ -130,7 +130,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)
|
||||
|
@ -191,7 +191,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)) {
|
||||
|
@ -205,7 +205,7 @@ static int smc_pnet_add_by_ndev(struct net_device *ndev)
|
|||
break;
|
||||
}
|
||||
}
|
||||
write_unlock(&pnettable->lock);
|
||||
mutex_unlock(&pnettable->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -223,7 +223,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(pnetelem->ndev);
|
||||
|
@ -236,7 +236,7 @@ static int smc_pnet_remove_by_ndev(struct net_device *ndev)
|
|||
break;
|
||||
}
|
||||
}
|
||||
write_unlock(&pnettable->lock);
|
||||
mutex_unlock(&pnettable->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -371,7 +371,7 @@ static int smc_pnet_add_eth(struct smc_pnettable *pnettable, struct net *net,
|
|||
|
||||
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)) {
|
||||
|
@ -381,9 +381,9 @@ static int smc_pnet_add_eth(struct smc_pnettable *pnettable, struct net *net,
|
|||
}
|
||||
if (new_netdev) {
|
||||
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;
|
||||
}
|
||||
|
@ -444,7 +444,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)) {
|
||||
|
@ -454,9 +454,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;
|
||||
|
@ -601,7 +601,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;
|
||||
|
@ -616,7 +616,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;
|
||||
}
|
||||
|
||||
|
@ -860,7 +860,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);
|
||||
|
||||
|
@ -940,7 +940,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 */
|
||||
|
@ -949,7 +949,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;
|
||||
}
|
||||
|
||||
|
@ -1130,7 +1130,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) &&
|
||||
|
@ -1140,7 +1140,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;
|
||||
}
|
||||
|
@ -1159,7 +1159,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)) {
|
||||
|
@ -1168,7 +1168,7 @@ int smc_pnetid_by_table_smcd(struct smcd_dev *smcddev)
|
|||
break;
|
||||
}
|
||||
}
|
||||
read_unlock(&pnettable->lock);
|
||||
mutex_unlock(&pnettable->lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче