Skip to content

Commit bdab167

Browse files
committed
Bluetooth: Fix TOCTOU in HCI debugfs implementation
jira LE-3201 cve CVE-2024-24857 Rebuild_History Non-Buildable kernel-rt-4.18.0-553.27.1.rt7.368.el8_10 commit-author Bastien Nocera <[email protected]> commit 7835fcf 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]> (cherry picked from commit 7835fcf) Signed-off-by: Jonathan Maple <[email protected]>
1 parent ac83b16 commit bdab167

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
@@ -217,10 +217,12 @@ static int conn_info_min_age_set(void *data, u64 val)
217217
{
218218
struct hci_dev *hdev = data;
219219

220-
if (val == 0 || val > hdev->conn_info_max_age)
220+
hci_dev_lock(hdev);
221+
if (val == 0 || val > hdev->conn_info_max_age) {
222+
hci_dev_unlock(hdev);
221223
return -EINVAL;
224+
}
222225

223-
hci_dev_lock(hdev);
224226
hdev->conn_info_min_age = val;
225227
hci_dev_unlock(hdev);
226228

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

248-
if (val == 0 || val < hdev->conn_info_min_age)
250+
hci_dev_lock(hdev);
251+
if (val == 0 || val < hdev->conn_info_min_age) {
252+
hci_dev_unlock(hdev);
249253
return -EINVAL;
254+
}
250255

251-
hci_dev_lock(hdev);
252256
hdev->conn_info_max_age = val;
253257
hci_dev_unlock(hdev);
254258

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

569-
if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
573+
hci_dev_lock(hdev);
574+
if (val == 0 || val % 2 || val > hdev->sniff_max_interval) {
575+
hci_dev_unlock(hdev);
570576
return -EINVAL;
577+
}
571578

572-
hci_dev_lock(hdev);
573579
hdev->sniff_min_interval = val;
574580
hci_dev_unlock(hdev);
575581

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

597-
if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
603+
hci_dev_lock(hdev);
604+
if (val == 0 || val % 2 || val < hdev->sniff_min_interval) {
605+
hci_dev_unlock(hdev);
598606
return -EINVAL;
607+
}
599608

600-
hci_dev_lock(hdev);
601609
hdev->sniff_max_interval = val;
602610
hci_dev_unlock(hdev);
603611

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

852-
if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
860+
hci_dev_lock(hdev);
861+
if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval) {
862+
hci_dev_unlock(hdev);
853863
return -EINVAL;
864+
}
854865

855-
hci_dev_lock(hdev);
856866
hdev->le_conn_min_interval = val;
857867
hci_dev_unlock(hdev);
858868

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

880-
if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
890+
hci_dev_lock(hdev);
891+
if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval) {
892+
hci_dev_unlock(hdev);
881893
return -EINVAL;
894+
}
882895

883-
hci_dev_lock(hdev);
884896
hdev->le_conn_max_interval = val;
885897
hci_dev_unlock(hdev);
886898

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

992-
if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval)
1004+
hci_dev_lock(hdev);
1005+
if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval) {
1006+
hci_dev_unlock(hdev);
9931007
return -EINVAL;
1008+
}
9941009

995-
hci_dev_lock(hdev);
9961010
hdev->le_adv_min_interval = val;
9971011
hci_dev_unlock(hdev);
9981012

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

1020-
if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval)
1034+
hci_dev_lock(hdev);
1035+
if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval) {
1036+
hci_dev_unlock(hdev);
10211037
return -EINVAL;
1038+
}
10221039

1023-
hci_dev_lock(hdev);
10241040
hdev->le_adv_max_interval = val;
10251041
hci_dev_unlock(hdev);
10261042

0 commit comments

Comments
 (0)