Skip to content

Commit 7835fcf

Browse files
hadessVudentz
authored andcommitted
Bluetooth: Fix TOCTOU in HCI debugfs implementation
struct hci_dev members conn_info_max_age, conn_info_min_age, le_conn_max_interval, le_conn_min_interval, le_adv_max_interval, and le_adv_min_interval can be modified from the HCI core code, as well through debugfs. The debugfs implementation, that's only available to privileged users, will check for boundaries, making sure that the minimum value being set is strictly above the maximum value that already exists, and vice-versa. However, as both minimum and maximum values can be changed concurrently to us modifying them, we need to make sure that the value we check is the value we end up using. For example, with ->conn_info_max_age set to 10, conn_info_min_age_set() gets called from vfs handlers to set conn_info_min_age to 8. In conn_info_min_age_set(), this goes through: if (val == 0 || val > hdev->conn_info_max_age) return -EINVAL; Concurrently, conn_info_max_age_set() gets called to set to set the conn_info_max_age to 7: if (val == 0 || val > hdev->conn_info_max_age) return -EINVAL; That check will also pass because we used the old value (10) for conn_info_max_age. After those checks that both passed, the struct hci_dev access is mutex-locked, disabling concurrent access, but that does not matter because the invalid value checks both passed, and we'll end up with conn_info_min_age = 8 and conn_info_max_age = 7 To fix this problem, we need to lock the structure access before so the check and assignment are not interrupted. This fix was originally devised by the BassCheck[1] team, and considered the problem to be an atomicity one. This isn't the case as there aren't any concerns about the variable changing while we check it, but rather after we check it parallel to another change. This patch fixes CVE-2024-24858 and CVE-2024-24857. [1] https://sites.google.com/view/basscheck/ Co-developed-by: Gui-Dong Han <[email protected]> Signed-off-by: Gui-Dong Han <[email protected]> Link: https://lore.kernel.org/linux-bluetooth/[email protected]/ Link: https://nvd.nist.gov/vuln/detail/CVE-2024-24858 Link: https://lore.kernel.org/linux-bluetooth/[email protected]/ Link: https://lore.kernel.org/linux-bluetooth/[email protected]/ Link: https://nvd.nist.gov/vuln/detail/CVE-2024-24857 Fixes: 31ad169 ("Bluetooth: Add conn info lifetime parameters to debugfs") Fixes: 729a105 ("Bluetooth: Expose default LE advertising interval via debugfs") Fixes: 71c3b60 ("Bluetooth: Move BR/EDR debugfs file creation into hci_debugfs.c") Signed-off-by: Bastien Nocera <[email protected]> Signed-off-by: Luiz Augusto von Dentz <[email protected]>
1 parent c569242 commit 7835fcf

File tree

1 file changed

+32
-16
lines changed

1 file changed

+32
-16
lines changed

net/bluetooth/hci_debugfs.c

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,12 @@ static int conn_info_min_age_set(void *data, u64 val)
218218
{
219219
struct hci_dev *hdev = data;
220220

221-
if (val == 0 || val > hdev->conn_info_max_age)
221+
hci_dev_lock(hdev);
222+
if (val == 0 || val > hdev->conn_info_max_age) {
223+
hci_dev_unlock(hdev);
222224
return -EINVAL;
225+
}
223226

224-
hci_dev_lock(hdev);
225227
hdev->conn_info_min_age = val;
226228
hci_dev_unlock(hdev);
227229

@@ -246,10 +248,12 @@ static int conn_info_max_age_set(void *data, u64 val)
246248
{
247249
struct hci_dev *hdev = data;
248250

249-
if (val == 0 || val < hdev->conn_info_min_age)
251+
hci_dev_lock(hdev);
252+
if (val == 0 || val < hdev->conn_info_min_age) {
253+
hci_dev_unlock(hdev);
250254
return -EINVAL;
255+
}
251256

252-
hci_dev_lock(hdev);
253257
hdev->conn_info_max_age = val;
254258
hci_dev_unlock(hdev);
255259

@@ -567,10 +571,12 @@ static int sniff_min_interval_set(void *data, u64 val)
567571
{
568572
struct hci_dev *hdev = data;
569573

570-
if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
574+
hci_dev_lock(hdev);
575+
if (val == 0 || val % 2 || val > hdev->sniff_max_interval) {
576+
hci_dev_unlock(hdev);
571577
return -EINVAL;
578+
}
572579

573-
hci_dev_lock(hdev);
574580
hdev->sniff_min_interval = val;
575581
hci_dev_unlock(hdev);
576582

@@ -595,10 +601,12 @@ static int sniff_max_interval_set(void *data, u64 val)
595601
{
596602
struct hci_dev *hdev = data;
597603

598-
if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
604+
hci_dev_lock(hdev);
605+
if (val == 0 || val % 2 || val < hdev->sniff_min_interval) {
606+
hci_dev_unlock(hdev);
599607
return -EINVAL;
608+
}
600609

601-
hci_dev_lock(hdev);
602610
hdev->sniff_max_interval = val;
603611
hci_dev_unlock(hdev);
604612

@@ -850,10 +858,12 @@ static int conn_min_interval_set(void *data, u64 val)
850858
{
851859
struct hci_dev *hdev = data;
852860

853-
if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
861+
hci_dev_lock(hdev);
862+
if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval) {
863+
hci_dev_unlock(hdev);
854864
return -EINVAL;
865+
}
855866

856-
hci_dev_lock(hdev);
857867
hdev->le_conn_min_interval = val;
858868
hci_dev_unlock(hdev);
859869

@@ -878,10 +888,12 @@ static int conn_max_interval_set(void *data, u64 val)
878888
{
879889
struct hci_dev *hdev = data;
880890

881-
if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
891+
hci_dev_lock(hdev);
892+
if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval) {
893+
hci_dev_unlock(hdev);
882894
return -EINVAL;
895+
}
883896

884-
hci_dev_lock(hdev);
885897
hdev->le_conn_max_interval = val;
886898
hci_dev_unlock(hdev);
887899

@@ -990,10 +1002,12 @@ static int adv_min_interval_set(void *data, u64 val)
9901002
{
9911003
struct hci_dev *hdev = data;
9921004

993-
if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval)
1005+
hci_dev_lock(hdev);
1006+
if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval) {
1007+
hci_dev_unlock(hdev);
9941008
return -EINVAL;
1009+
}
9951010

996-
hci_dev_lock(hdev);
9971011
hdev->le_adv_min_interval = val;
9981012
hci_dev_unlock(hdev);
9991013

@@ -1018,10 +1032,12 @@ static int adv_max_interval_set(void *data, u64 val)
10181032
{
10191033
struct hci_dev *hdev = data;
10201034

1021-
if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval)
1035+
hci_dev_lock(hdev);
1036+
if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval) {
1037+
hci_dev_unlock(hdev);
10221038
return -EINVAL;
1039+
}
10231040

1024-
hci_dev_lock(hdev);
10251041
hdev->le_adv_max_interval = val;
10261042
hci_dev_unlock(hdev);
10271043

0 commit comments

Comments
 (0)