aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLin Ma2021-11-16 09:26:52 -0600
committerGreg Kroah-Hartman2021-11-26 04:58:44 -0600
commit5ef16d2d172ee56714cff37cd005b98aba08ef5a (patch)
treeaa3b64439ca0e0ede96a30bb79f0e028bd0decfc
parent6dc051117ba0e1dac9324593ff2c1c520f67ad21 (diff)
downloadti-linux-kernel-5ef16d2d172ee56714cff37cd005b98aba08ef5a.tar.gz
ti-linux-kernel-5ef16d2d172ee56714cff37cd005b98aba08ef5a.tar.xz
ti-linux-kernel-5ef16d2d172ee56714cff37cd005b98aba08ef5a.zip
NFC: reorder the logic in nfc_{un,}register_device
[ Upstream commit 3e3b5dfcd16a3e254aab61bd1e8c417dd4503102 ] There is a potential UAF between the unregistration routine and the NFC netlink operations. The race that cause that UAF can be shown as below: (FREE) | (USE) nfcmrvl_nci_unregister_dev | nfc_genl_dev_up nci_close_device | nci_unregister_device | nfc_get_device nfc_unregister_device | nfc_dev_up rfkill_destory | device_del | rfkill_blocked ... | ... The root cause for this race is concluded below: 1. The rfkill_blocked (USE) in nfc_dev_up is supposed to be placed after the device_is_registered check. 2. Since the netlink operations are possible just after the device_add in nfc_register_device, the nfc_dev_up() can happen anywhere during the rfkill creation process, which leads to data race. This patch reorder these actions to permit 1. Once device_del is finished, the nfc_dev_up cannot dereference the rfkill object. 2. The rfkill_register need to be placed after the device_add of nfc_dev because the parent device need to be created first. So this patch keeps the order but inject device_lock to prevent the data race. Signed-off-by: Lin Ma <linma@zju.edu.cn> Fixes: be055b2f89b5 ("NFC: RFKILL support") Reviewed-by: Jakub Kicinski <kuba@kernel.org> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com> Link: https://lore.kernel.org/r/20211116152652.19217-1-linma@zju.edu.cn Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
-rw-r--r--net/nfc/core.c32
1 files changed, 18 insertions, 14 deletions
diff --git a/net/nfc/core.c b/net/nfc/core.c
index 1471e4b0aa2c..8c7f221e1d12 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -106,13 +106,13 @@ int nfc_dev_up(struct nfc_dev *dev)
106 106
107 device_lock(&dev->dev); 107 device_lock(&dev->dev);
108 108
109 if (dev->rfkill && rfkill_blocked(dev->rfkill)) { 109 if (!device_is_registered(&dev->dev)) {
110 rc = -ERFKILL; 110 rc = -ENODEV;
111 goto error; 111 goto error;
112 } 112 }
113 113
114 if (!device_is_registered(&dev->dev)) { 114 if (dev->rfkill && rfkill_blocked(dev->rfkill)) {
115 rc = -ENODEV; 115 rc = -ERFKILL;
116 goto error; 116 goto error;
117 } 117 }
118 118
@@ -1120,11 +1120,7 @@ int nfc_register_device(struct nfc_dev *dev)
1120 if (rc) 1120 if (rc)
1121 pr_err("Could not register llcp device\n"); 1121 pr_err("Could not register llcp device\n");
1122 1122
1123 rc = nfc_genl_device_added(dev); 1123 device_lock(&dev->dev);
1124 if (rc)
1125 pr_debug("The userspace won't be notified that the device %s was added\n",
1126 dev_name(&dev->dev));
1127
1128 dev->rfkill = rfkill_alloc(dev_name(&dev->dev), &dev->dev, 1124 dev->rfkill = rfkill_alloc(dev_name(&dev->dev), &dev->dev,
1129 RFKILL_TYPE_NFC, &nfc_rfkill_ops, dev); 1125 RFKILL_TYPE_NFC, &nfc_rfkill_ops, dev);
1130 if (dev->rfkill) { 1126 if (dev->rfkill) {
@@ -1133,6 +1129,12 @@ int nfc_register_device(struct nfc_dev *dev)
1133 dev->rfkill = NULL; 1129 dev->rfkill = NULL;
1134 } 1130 }
1135 } 1131 }
1132 device_unlock(&dev->dev);
1133
1134 rc = nfc_genl_device_added(dev);
1135 if (rc)
1136 pr_debug("The userspace won't be notified that the device %s was added\n",
1137 dev_name(&dev->dev));
1136 1138
1137 return 0; 1139 return 0;
1138} 1140}
@@ -1149,10 +1151,17 @@ void nfc_unregister_device(struct nfc_dev *dev)
1149 1151
1150 pr_debug("dev_name=%s\n", dev_name(&dev->dev)); 1152 pr_debug("dev_name=%s\n", dev_name(&dev->dev));
1151 1153
1154 rc = nfc_genl_device_removed(dev);
1155 if (rc)
1156 pr_debug("The userspace won't be notified that the device %s "
1157 "was removed\n", dev_name(&dev->dev));
1158
1159 device_lock(&dev->dev);
1152 if (dev->rfkill) { 1160 if (dev->rfkill) {
1153 rfkill_unregister(dev->rfkill); 1161 rfkill_unregister(dev->rfkill);
1154 rfkill_destroy(dev->rfkill); 1162 rfkill_destroy(dev->rfkill);
1155 } 1163 }
1164 device_unlock(&dev->dev);
1156 1165
1157 if (dev->ops->check_presence) { 1166 if (dev->ops->check_presence) {
1158 device_lock(&dev->dev); 1167 device_lock(&dev->dev);
@@ -1162,11 +1171,6 @@ void nfc_unregister_device(struct nfc_dev *dev)
1162 cancel_work_sync(&dev->check_pres_work); 1171 cancel_work_sync(&dev->check_pres_work);
1163 } 1172 }
1164 1173
1165 rc = nfc_genl_device_removed(dev);
1166 if (rc)
1167 pr_debug("The userspace won't be notified that the device %s "
1168 "was removed\n", dev_name(&dev->dev));
1169
1170 nfc_llcp_unregister_device(dev); 1174 nfc_llcp_unregister_device(dev);
1171 1175
1172 mutex_lock(&nfc_devlist_mutex); 1176 mutex_lock(&nfc_devlist_mutex);