Skip to content

Commit 3e3b5df

Browse files
f0rm2l1nkuba-moo
authored andcommitted
NFC: reorder the logic in nfc_{un,}register_device
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: be055b2 ("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>
1 parent 86cdf8e commit 3e3b5df

File tree

1 file changed

+18
-14
lines changed

1 file changed

+18
-14
lines changed

net/nfc/core.c

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,13 @@ int nfc_dev_up(struct nfc_dev *dev)
9494

9595
device_lock(&dev->dev);
9696

97-
if (dev->rfkill && rfkill_blocked(dev->rfkill)) {
98-
rc = -ERFKILL;
97+
if (!device_is_registered(&dev->dev)) {
98+
rc = -ENODEV;
9999
goto error;
100100
}
101101

102-
if (!device_is_registered(&dev->dev)) {
103-
rc = -ENODEV;
102+
if (dev->rfkill && rfkill_blocked(dev->rfkill)) {
103+
rc = -ERFKILL;
104104
goto error;
105105
}
106106

@@ -1125,11 +1125,7 @@ int nfc_register_device(struct nfc_dev *dev)
11251125
if (rc)
11261126
pr_err("Could not register llcp device\n");
11271127

1128-
rc = nfc_genl_device_added(dev);
1129-
if (rc)
1130-
pr_debug("The userspace won't be notified that the device %s was added\n",
1131-
dev_name(&dev->dev));
1132-
1128+
device_lock(&dev->dev);
11331129
dev->rfkill = rfkill_alloc(dev_name(&dev->dev), &dev->dev,
11341130
RFKILL_TYPE_NFC, &nfc_rfkill_ops, dev);
11351131
if (dev->rfkill) {
@@ -1138,6 +1134,12 @@ int nfc_register_device(struct nfc_dev *dev)
11381134
dev->rfkill = NULL;
11391135
}
11401136
}
1137+
device_unlock(&dev->dev);
1138+
1139+
rc = nfc_genl_device_added(dev);
1140+
if (rc)
1141+
pr_debug("The userspace won't be notified that the device %s was added\n",
1142+
dev_name(&dev->dev));
11411143

11421144
return 0;
11431145
}
@@ -1154,10 +1156,17 @@ void nfc_unregister_device(struct nfc_dev *dev)
11541156

11551157
pr_debug("dev_name=%s\n", dev_name(&dev->dev));
11561158

1159+
rc = nfc_genl_device_removed(dev);
1160+
if (rc)
1161+
pr_debug("The userspace won't be notified that the device %s "
1162+
"was removed\n", dev_name(&dev->dev));
1163+
1164+
device_lock(&dev->dev);
11571165
if (dev->rfkill) {
11581166
rfkill_unregister(dev->rfkill);
11591167
rfkill_destroy(dev->rfkill);
11601168
}
1169+
device_unlock(&dev->dev);
11611170

11621171
if (dev->ops->check_presence) {
11631172
device_lock(&dev->dev);
@@ -1167,11 +1176,6 @@ void nfc_unregister_device(struct nfc_dev *dev)
11671176
cancel_work_sync(&dev->check_pres_work);
11681177
}
11691178

1170-
rc = nfc_genl_device_removed(dev);
1171-
if (rc)
1172-
pr_debug("The userspace won't be notified that the device %s "
1173-
"was removed\n", dev_name(&dev->dev));
1174-
11751179
nfc_llcp_unregister_device(dev);
11761180

11771181
mutex_lock(&nfc_devlist_mutex);

0 commit comments

Comments
 (0)